Repository: glushchenko/fsnotes Branch: master Commit: 866f59188866 Files: 487 Total size: 5.7 MB Directory structure: gitextract_5ehxlhed/ ├── .gitattributes ├── .github/ │ └── ISSUE_TEMPLATE/ │ └── bug_report.yml ├── .gitignore ├── .swiftlint.yml ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── FSNotes/ │ ├── .bartycrouch.toml │ ├── AboutViewController.swift │ ├── AboutWindowController.swift │ ├── AppDelegate+URLRoutes.swift │ ├── AppDelegate.swift │ ├── Base.lproj/ │ │ └── Main.storyboard │ ├── EditorViewController+ScrollPosition.swift │ ├── EditorViewController+Sharing.swift │ ├── EditorViewController.swift │ ├── Extensions/ │ │ ├── NSAppearance+.swift │ │ ├── NSColor+.swift │ │ ├── NSFont+.swift │ │ ├── NSImage+.swift │ │ ├── NSWindow+.swift │ │ └── UserDefaultsManagement+.swift │ ├── FSNotes (CloudKit).entitlements │ ├── FSNotes.entitlements │ ├── Helpers/ │ │ ├── FSNTextAttachmentCell.swift │ │ ├── FileSystemEventManager.swift │ │ ├── FileWatcher.swift │ │ ├── FileWatcherEvent.swift │ │ ├── Printer.swift │ │ ├── PrinterLegacy.swift │ │ ├── SandboxBookmark.swift │ │ ├── Sidebar.swift │ │ └── UserDataService.swift │ ├── Images.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── Icons/ │ │ │ ├── AppIconClassic.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── AppIconModern.imageset/ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── checkbox_empty.imageset/ │ │ │ └── Contents.json │ │ ├── checkbox_flipped.imageset/ │ │ │ └── Contents.json │ │ ├── checkbox_new.imageset/ │ │ │ └── Contents.json │ │ ├── code.colorset/ │ │ │ └── Contents.json │ │ ├── colors_background/ │ │ │ ├── Contents.json │ │ │ ├── background_tag.colorset/ │ │ │ │ └── Contents.json │ │ │ └── background_win.colorset/ │ │ │ └── Contents.json │ │ ├── copy.png.imageset/ │ │ │ └── Contents.json │ │ ├── divider.colorset/ │ │ │ └── Contents.json │ │ ├── dockIcon2.imageset/ │ │ │ └── Contents.json │ │ ├── dockIcon4.imageset/ │ │ │ └── Contents.json │ │ ├── friend.imageset/ │ │ │ └── Contents.json │ │ ├── highlight.colorset/ │ │ │ └── Contents.json │ │ ├── link.colorset/ │ │ │ └── Contents.json │ │ ├── locked.imageset/ │ │ │ └── Contents.json │ │ ├── mainBackground.colorset/ │ │ │ └── Contents.json │ │ ├── mainText.colorset/ │ │ │ └── Contents.json │ │ ├── menuBar.imageset/ │ │ │ └── Contents.json │ │ ├── new_note_button.imageset/ │ │ │ └── Contents.json │ │ ├── pin.imageset/ │ │ │ └── Contents.json │ │ ├── prefsAdvanced.imageset/ │ │ │ └── Contents.json │ │ ├── prefsEditor.imageset/ │ │ │ └── Contents.json │ │ ├── prefsGeneral.imageset/ │ │ │ └── Contents.json │ │ ├── prefsGit.imageset/ │ │ │ └── Contents.json │ │ ├── prefsLayout.imageset/ │ │ │ └── Contents.json │ │ ├── prefsWeb.imageset/ │ │ │ └── Contents.json │ │ ├── privacy.imageset/ │ │ │ └── Contents.json │ │ ├── quoteColor.colorset/ │ │ │ └── Contents.json │ │ ├── reverseBackground.colorset/ │ │ │ └── Contents.json │ │ ├── sidebar_archive.imageset/ │ │ │ └── Contents.json │ │ ├── sidebar_external.imageset/ │ │ │ └── Contents.json │ │ ├── sidebar_icloud_drive.imageset/ │ │ │ └── Contents.json │ │ ├── sidebar_inbox.imageset/ │ │ │ └── Contents.json │ │ ├── sidebar_notes.imageset/ │ │ │ └── Contents.json │ │ ├── sidebar_project.imageset/ │ │ │ └── Contents.json │ │ ├── sidebar_project_encrypted_locked.imageset/ │ │ │ └── Contents.json │ │ ├── sidebar_project_encrypted_unlocked.imageset/ │ │ │ └── Contents.json │ │ ├── sidebar_tag.imageset/ │ │ │ └── Contents.json │ │ ├── sidebar_todo.imageset/ │ │ │ └── Contents.json │ │ ├── sidebar_trash.imageset/ │ │ │ └── Contents.json │ │ ├── sidebar_untagged.imageset/ │ │ │ └── Contents.json │ │ └── web.imageset/ │ │ └── Contents.json │ ├── Info.plist │ ├── LayoutManager.swift │ ├── Localizable.xcstrings │ ├── MainWindow.swift │ ├── MainWindowController.swift │ ├── Model/ │ │ ├── StorageEntity+CoreDataClass.swift │ │ └── StorageEntity+CoreDataProperties.swift │ ├── NSWindowController+.swift │ ├── NoteViewController.swift │ ├── Preferences/ │ │ ├── MasterPasswordViewController.swift │ │ ├── PreferencesAdvancedViewController.swift │ │ ├── PreferencesEditorViewController.swift │ │ ├── PreferencesGeneralViewController.swift │ │ ├── PreferencesGitViewController.swift │ │ ├── PreferencesSecurityViewController.swift │ │ ├── PreferencesUserInterfaceViewController.swift │ │ ├── PreferencesWebViewController.swift │ │ └── SettingsViewController.swift │ ├── PrefsViewController.swift │ ├── PrefsWindowController.swift │ ├── ProjectSettingsViewController.swift │ ├── SidebarScrollView.swift │ ├── View/ │ │ ├── AboutImageView.swift │ │ ├── ClickableTextField.swift │ │ ├── EditTextView+Clicked.swift │ │ ├── EditTextView+Complete.swift │ │ ├── EditTextView+DragOperation.swift │ │ ├── EditTextView+MoveLines.swift │ │ ├── EditTextView+Todo.swift │ │ ├── EditTextView.swift │ │ ├── EditorScrollView.swift │ │ ├── EditorSplitView.swift │ │ ├── EditorView.swift │ │ ├── HyperlinkTextField.swift │ │ ├── MPreviewContainerView.swift │ │ ├── MPreviewFindPanel.swift │ │ ├── NameTextField.swift │ │ ├── NoteCellView.swift │ │ ├── NoteRowView.swift │ │ ├── NotesCounterView.swift │ │ ├── NotesTableView.swift │ │ ├── OutlineHeaderView.swift │ │ ├── PreviewTextField.swift │ │ ├── SearchTextField.swift │ │ ├── SidebarCellView.swift │ │ ├── SidebarHeaderCellView.swift │ │ ├── SidebarNotesView.swift │ │ ├── SidebarOutlineView.swift │ │ ├── SidebarSplitView.swift │ │ ├── SidebarTableRowView.swift │ │ ├── TitleBarView.swift │ │ ├── TitleTextField.swift │ │ └── VerticallyAlignedTextFieldCell.swift │ ├── ViewController+Git.swift │ ├── ViewController+Menu.swift │ ├── ViewController+Print.swift │ ├── ViewController+Web.swift │ ├── ViewController.swift │ ├── bin/ │ │ └── git │ ├── modern.icon/ │ │ └── icon.json │ └── mul.lproj/ │ └── Main.xcstrings ├── FSNotes Info (Notarized).plist ├── FSNotes iOS/ │ ├── .bartycrouch.toml │ ├── AppDelegate.swift │ ├── Assets.xcassets/ │ │ ├── Colors/ │ │ │ ├── Contents.json │ │ │ └── fsColor.colorset/ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── Editor/ │ │ │ ├── Contents.json │ │ │ ├── checkbox.imageset/ │ │ │ │ └── Contents.json │ │ │ └── checkbox_empty.imageset/ │ │ │ └── Contents.json │ │ ├── Icons/ │ │ │ ├── AppIconClassic-2025.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── AppIconModern.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── AppIconNy-2026.imageset/ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── LaunchScreenImage.imageset/ │ │ │ └── Contents.json │ │ ├── Sidebar/ │ │ │ ├── Contents.json │ │ │ ├── sidebar_archive.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── sidebar_inbox.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── sidebar_notes.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── sidebar_project.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── sidebar_project_encrypted_locked.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── sidebar_project_encrypted_unlocked.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── sidebar_tag.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── sidebar_todo.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── sidebar_trash.imageset/ │ │ │ │ └── Contents.json │ │ │ └── sidebar_untagged.imageset/ │ │ │ └── Contents.json │ │ ├── Sidebar Actions/ │ │ │ ├── Contents.json │ │ │ └── gitSettings.imageset/ │ │ │ └── Contents.json │ │ ├── Toolbar/ │ │ │ ├── Contents.json │ │ │ ├── codeBlockAsset.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── numbered_list.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── ordered_list.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── pictureAsset.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── quote.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── redo.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── toolbarBold.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── toolbarHeader.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── toolbarImage.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── toolbarIndentLeft.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── toolbarIndentRight.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── toolbarItalic.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── toolbarTag.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── toolbarTodo.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── toolbarWiki.imageset/ │ │ │ │ └── Contents.json │ │ │ └── undo.imageset/ │ │ │ └── Contents.json │ │ └── Touch Bar/ │ │ ├── Contents.json │ │ ├── Image.imageset/ │ │ │ └── Contents.json │ │ ├── bold.imageset/ │ │ │ └── Contents.json │ │ ├── codeblock.imageset/ │ │ │ └── Contents.json │ │ ├── indent.imageset/ │ │ │ └── Contents.json │ │ ├── italic.imageset/ │ │ │ └── Contents.json │ │ ├── tb_link.imageset/ │ │ │ └── Contents.json │ │ ├── todo.imageset/ │ │ │ └── Contents.json │ │ └── unindent.imageset/ │ │ └── Contents.json │ ├── DatePickerViewController.swift │ ├── EditorViewController+QuickLook.swift │ ├── EditorViewController+Search.swift │ ├── EditorViewController.swift │ ├── Extensions/ │ │ ├── UIApplication+.swift │ │ ├── UIBarButtonItem+.swift │ │ ├── UIColor+.swift │ │ ├── UIFont+.swift │ │ ├── UIImage+.swift │ │ ├── UITextView+.swift │ │ └── UserDefaultsManagement+.swift │ ├── FSNotes iOS.entitlements │ ├── FSNotes_iOS.xcdatamodeld/ │ │ ├── .xccurrentversion │ │ └── FSNotes_iOS.xcdatamodel/ │ │ └── contents │ ├── Helpers/ │ │ ├── Buttons.swift │ │ ├── CloudDriveManager.swift │ │ ├── FolderPopoverActions.swift │ │ ├── SandboxBookmark.swift │ │ ├── ShortcutIdentifier.swift │ │ ├── Sidebar.swift │ │ ├── SingleImageTouchDownGestureRecognizer.swift │ │ └── SingleTouchDownGestureRecognizer.swift │ ├── Icons/ │ │ ├── classic-2025.icon/ │ │ │ └── icon.json │ │ ├── modern.icon/ │ │ │ └── icon.json │ │ └── ny-2026.icon/ │ │ └── icon.json │ ├── ImagePreviewViewController.swift │ ├── Info.plist │ ├── InfoPlist.xcstrings │ ├── Launch Screen.storyboard │ ├── LaunchImage.launchimage/ │ │ └── Contents.json │ ├── Localizable.xcstrings │ ├── Main.storyboard │ ├── MainNavigationController.swift │ ├── MoveViewController.swift │ ├── Preferences/ │ │ ├── AppIconViewController.swift │ │ ├── CodeFontViewController.swift │ │ ├── CodeThemeViewController.swift │ │ ├── DefaultExtensionControllerView.swift │ │ ├── ExternalViewController.swift │ │ ├── FontViewController.swift │ │ ├── GitTableViewCell.swift │ │ ├── GitViewController.swift │ │ ├── LanguageViewController.swift │ │ ├── ProViewController.swift │ │ ├── ProjectSettingsViewController.swift │ │ ├── ProjectsViewController.swift │ │ ├── SecurityViewController.swift │ │ ├── SettingsEditorViewController.swift │ │ ├── SettingsTableViewCell.swift │ │ ├── SettingsViewController.swift │ │ ├── SidebarViewController.swift │ │ ├── SortByViewController.swift │ │ └── ThanksViewController.swift │ ├── RevisionsViewController.swift │ ├── SceneDelegate.swift │ ├── View/ │ │ ├── EditTextView.swift │ │ ├── EditorSelectionRect.swift │ │ ├── ImageScrollView.swift │ │ ├── NoteCellView.swift │ │ ├── NotesTableView.swift │ │ ├── SidebarTableCellView.swift │ │ └── SidebarTableView.swift │ ├── ViewController+More.swift │ ├── ViewController.swift │ ├── fr.lproj/ │ │ └── Main.storyboard │ ├── nl-NL.lproj/ │ │ └── Main.storyboard │ ├── pt-PT.lproj/ │ │ └── Main.storyboard │ ├── ru.lproj/ │ │ ├── InfoPlist.strings │ │ ├── LaunchScreen.strings │ │ ├── Localizable.strings │ │ └── Main.storyboard │ └── uk.lproj/ │ └── Main.storyboard ├── FSNotes iOS Share/ │ ├── .bartycrouch.toml │ ├── FSNotes iOS Share.entitlements │ ├── Info.plist │ ├── Localizable.xcstrings │ ├── MainInterface.storyboard │ ├── NSMutableAttributedString+.swift │ ├── ShareViewController.swift │ ├── es.lproj/ │ │ └── Localizable.strings │ ├── pt.lproj/ │ │ └── InfoPlist.strings │ ├── ru.lproj/ │ │ ├── InfoPlist.strings │ │ ├── Localizable.strings │ │ └── MainInterface.strings │ ├── uk.lproj/ │ │ ├── InfoPlist.strings │ │ └── MainInterface.strings │ ├── zh-Hans-CN.lproj/ │ │ ├── InfoPlist.strings │ │ └── Localizable.strings │ └── zh-Hans.lproj/ │ └── MainInterface.strings ├── FSNotes.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ ├── IDEWorkspaceChecks.plist │ │ ├── WorkspaceSettings.xcsettings │ │ └── swiftpm/ │ │ └── Package.resolved │ └── xcshareddata/ │ └── xcschemes/ │ ├── FSNotes (iCloud).xcscheme │ ├── FSNotes iOS Share Extension.xcscheme │ ├── FSNotes iOS.xcscheme │ └── FSNotes.xcscheme ├── FSNotes.xcworkspace/ │ └── contents.xcworkspacedata ├── FSNotesCore/ │ ├── Business/ │ │ ├── ApiResponse.swift │ │ ├── AppearanceType.swift │ │ ├── AttributedBox.swift │ │ ├── FSTag.swift │ │ ├── ImageFormat.swift │ │ ├── LanguageType.swift │ │ ├── Markdown.swift │ │ ├── Note+Preview.swift │ │ ├── Note.swift │ │ ├── NoteAttachment.swift │ │ ├── NoteContainer.swift │ │ ├── NoteType.swift │ │ ├── PreviewState.swift │ │ ├── ProgressState.swift │ │ ├── Project+Date.swift │ │ ├── Project.swift │ │ ├── ProjectSettings.swift │ │ ├── RuntimeError.swift │ │ ├── SearchQuery.swift │ │ ├── SettingsFilesNaming.swift │ │ ├── SidebarItem.swift │ │ ├── SidebarItemType.swift │ │ ├── SortBy.swift │ │ ├── SortDirection.swift │ │ ├── Storage.swift │ │ ├── StorageType.swift │ │ ├── TextBundleInfo.swift │ │ └── UndoData.swift │ ├── CodeBlockDetector.swift │ ├── Core macOS/ │ │ └── zh-Hans-CN.lproj/ │ │ └── InfoPlist.strings │ ├── Extensions/ │ │ ├── Data+.swift │ │ ├── Date+.swift │ │ ├── DateFormatter+.swift │ │ ├── FileManager+.swift │ │ ├── NSAttributedString+.swift │ │ ├── NSAttributedStringKey+.swift │ │ ├── NSMutableAttributedString+.swift │ │ ├── NSRange+.swift │ │ ├── NSTextCheckingResult+.swift │ │ ├── Pasteboard.swift │ │ ├── Project+Git.swift │ │ ├── Storage+Git.swift │ │ ├── String+.swift │ │ ├── String+Punycode.swift │ │ ├── URL+.swift │ │ ├── URL+Image.swift │ │ └── UTI.swift │ ├── FSParser.swift │ ├── Git/ │ │ ├── authentication/ │ │ │ ├── Authentication.swift │ │ │ ├── KeyAuthentication.swift │ │ │ └── PasswordAuthentication.swift │ │ ├── branch/ │ │ │ ├── Branch.swift │ │ │ ├── Branches.swift │ │ │ └── BranchesIterator.swift │ │ ├── commit/ │ │ │ └── Commit.swift │ │ ├── commons/ │ │ │ ├── Blob.swift │ │ │ ├── ConfigManager.swift │ │ │ ├── Error.swift │ │ │ ├── Errors.swift │ │ │ ├── OID.swift │ │ │ ├── Object.swift │ │ │ ├── Progress.swift │ │ │ ├── Signature.swift │ │ │ ├── StaticSshKeyDelegate.swift │ │ │ ├── Strings.swift │ │ │ └── Wrapper.swift │ │ ├── diff/ │ │ │ ├── Diff.swift │ │ │ └── DiffEntry.swift │ │ ├── head/ │ │ │ ├── Head+Checkout.swift │ │ │ ├── Head+Merge.swift │ │ │ └── Head.swift │ │ ├── index/ │ │ │ ├── Index+Commit.swift │ │ │ ├── Index+Files.swift │ │ │ └── Index.swift │ │ ├── reference/ │ │ │ ├── Reference+Target.swift │ │ │ └── Reference.swift │ │ ├── remote/ │ │ │ ├── Remote.swift │ │ │ └── Remotes.swift │ │ ├── repository/ │ │ │ ├── Repository+Commit.swift │ │ │ ├── Repository+Lookup.swift │ │ │ ├── Repository+Open.swift │ │ │ ├── Repository.swift │ │ │ └── RepositoryManager.swift │ │ ├── revision/ │ │ │ ├── FileHistoryIterator.swift │ │ │ └── RevisionIterator.swift │ │ ├── status/ │ │ │ ├── Status.swift │ │ │ ├── StatusIterator.swift │ │ │ └── Statuses.swift │ │ ├── tag/ │ │ │ ├── Tag.swift │ │ │ ├── TagIterator.swift │ │ │ └── Tags.swift │ │ └── tree/ │ │ ├── Tree.swift │ │ └── TreeEntry.swift │ ├── HtmlExtractor.swift │ ├── ImagesProcessor.swift │ ├── KeychainConfiguration.swift │ ├── KeychainPasswordItem.swift │ ├── MPreviewView.swift │ ├── NSTextAttachment+.swift │ ├── NSTextStorage++.swift │ ├── NameHelper.swift │ ├── Note+History.swift │ ├── NoteCellView+.swift │ ├── NoteMeta.swift │ ├── NotesTextProcessor.swift │ ├── RepositoryAction.swift │ ├── SwiftHighlighter/ │ │ ├── Languages/ │ │ │ ├── Assembly.swift │ │ │ ├── Bash.swift │ │ │ ├── C.swift │ │ │ ├── Clojure.swift │ │ │ ├── Cpp.swift │ │ │ ├── Csharp.swift │ │ │ ├── Css.swift │ │ │ ├── Dart.swift │ │ │ ├── Erlang.swift │ │ │ ├── Go.swift │ │ │ ├── Groovy.swift │ │ │ ├── Haskell.swift │ │ │ ├── Html.swift │ │ │ ├── Java.swift │ │ │ ├── JavaScript.swift │ │ │ ├── Kotlin.swift │ │ │ ├── Lisp.swift │ │ │ ├── Lua.swift │ │ │ ├── Matlab.swift │ │ │ ├── Mermaid.swift │ │ │ ├── ObjectiveC.swift │ │ │ ├── Perl.swift │ │ │ ├── Php.swift │ │ │ ├── Python.swift │ │ │ ├── R.swift │ │ │ ├── Ruby.swift │ │ │ ├── Rust.swift │ │ │ ├── Scala.swift │ │ │ ├── Scratch.swift │ │ │ ├── Shell.swift │ │ │ ├── Sql.swift │ │ │ ├── Swift.swift │ │ │ ├── TypeScript.swift │ │ │ └── Vb.swift │ │ ├── Platform.swift │ │ ├── SwiftHighlighter.swift │ │ ├── Theme.swift │ │ └── Themes/ │ │ ├── AtomOneDark.swift │ │ ├── AtomOneLight.swift │ │ ├── GitHubDark.swift │ │ ├── GitHubLight.swift │ │ ├── SolarizedDark.swift │ │ └── SolarizedLight.swift │ ├── TextFormatter.swift │ ├── TextStorageProcessor.swift │ ├── UserDefaultsManagement.swift │ └── ViewController+WebApi.swift ├── LICENSE ├── Logo/ │ └── License ├── Podfile ├── README.md ├── README_zh_CN.md ├── README_zh_TW.md └── Resources/ ├── Icons/ │ ├── EncryptedTextPack.icns │ ├── Markdown.icns │ ├── Text.icns │ └── TextBundle.icns ├── Initial/ │ ├── FSNotes - Readme.md │ ├── FSNotes 4.0 Change Log.textbundle/ │ │ ├── info.json │ │ └── text.markdown │ ├── FSNotes 4.0 for iOS.textbundle/ │ │ ├── info.json │ │ └── text.markdown │ ├── FSNotes 5.0 Change Log.textbundle/ │ │ ├── info.json │ │ └── text.md │ ├── Meet FSNotes 6.textbundle/ │ │ ├── info.json │ │ └── text.markdown │ └── Meet FSNotes 7.textbundle/ │ ├── info.json │ └── text.markdown ├── MPreview.bundle/ │ ├── index.html │ ├── js/ │ │ └── tex-mml-chtml.js │ └── main.css └── Welcome.bundle/ ├── 1. Introduction.textbundle/ │ ├── info.json │ └── text.markdown ├── 2. Links.textbundle/ │ ├── info.json │ └── text.markdown ├── 3. Shortcuts.textbundle/ │ ├── info.json │ └── text.markdown ├── 4. Sidebar.textbundle/ │ ├── info.json │ └── text.markdown ├── 5. Tags and subtags.textbundle/ │ ├── info.json │ └── text.markdown ├── 6. Mermaid and MathJax.textbundle/ │ ├── info.json │ └── text.markdown ├── 7. Git powered versioning.textbundle/ │ ├── info.json │ └── text.markdown ├── 8. Containers.textbundle/ │ ├── info.json │ └── text.markdown └── 9. GFM Markdown.textbundle/ ├── info.json └── text.markdown ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ *.pbxproj merge=union ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: Bug report description: Create a report to help us improve labels: 'bug' body: - type: textarea attributes: label: Description description: >- A clear and concise description of what the bug is. validations: required: true - type: textarea attributes: label: To Reproduce description: >- Steps to reproduce the behavior. value: | 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error validations: required: true - type: textarea attributes: label: Expected behavior description: >- A clear and concise description of what you expected to happen. - type: input attributes: label: FSNotes version validations: required: true - type: input attributes: label: macOS/iOS version validations: required: true - type: textarea attributes: label: Additional context description: >- Add any other context about the problem here. ================================================ FILE: .gitignore ================================================ Pods *.xcuserstate xcuserdata Podfile.lock FSNotes.xcworkspace/xcshareddata/* FSNotes.xcworkspace/xcuserdata/* FSNotes.xcodeproj/xcuserdata/* ================================================ FILE: .swiftlint.yml ================================================ included: # paths to include during linting. `--path` is ignored if present. - FSNotesCore excluded: # paths to ignore during linting. Takes precedence over `included`. - Pods line_length: warning: 220 disabled_rules: - implicit_getter - identifier_name cyclomatic_complexity: 20 function_body_length: 100 file_length: 1000 ================================================ FILE: .travis.yml ================================================ language: swift os: osx osx_image: xcode11.5 xcode_workspace: FSNotes.xcworkspace before_install: - pod install --repo-update env: - SCHEME=FSNotes SDK=macosx - SCHEME="FSNotes iOS" SDK=iphonesimulator script: xcodebuild -workspace FSNotes.xcworkspace -scheme "$SCHEME" build -sdk $SDK ONLY_ACTIVE_ARCH=NO -UseModernBuildSystem=NO -quiet branches: only: [master] ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at fluder (at) icloud (dot) com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq ================================================ FILE: FSNotes/.bartycrouch.toml ================================================ [update] tasks = ["interfaces", "code", "transform", "normalize"] [update.interfaces] paths = ["."] defaultToBase = true ignoreEmptyStrings = false unstripped = false [update.code] codePaths = ["."] localizablePaths = ["."] defaultToKeys = true additive = true unstripped = false plistArguments = true [update.transform] codePaths = ["."] localizablePaths = ["."] transformer = "foundation" supportedLanguageEnumPath = "." typeName = "BartyCrouch" translateMethodName = "translate" [update.normalize] paths = ["."] sourceLocale = "en" harmonizeWithSource = true sortByKeys = true [lint] paths = ["."] duplicateKeys = false emptyValues = true ================================================ FILE: FSNotes/AboutViewController.swift ================================================ // // AboutViewController.swift // FSNotes // // Created by Олександр Глущенко on 5/10/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Cocoa class AboutViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate { @IBOutlet weak var translatorsList: NSTableView! private var languages = [ "Deutsch 🇩🇪", "Ukrainian🇺🇦", "Spanish 🇪🇸", "Arabic 🇮🇶", "Chinese 🇨🇳", "Korean 🇰🇷", "French 🇫🇷", "Dutch 🇳🇱", "Portuguese 🇵🇹", "Italian 🇮🇹", "Hebrew 🇮🇱", "Chinese 🇨🇳", "Portuguese 🇵🇹", "Czech 🇨🇿", "Hindi 🇮🇳", "Turkish 🇹🇷", "Chinese 🇹🇼🇭🇰🇲🇴" ] private var authors = [ "Michael Barzmann", "Olena Hlushchenko ♥️", "aonez (aone@keka.io)", "Ayad (@ayad0net)", "Pertim (macwk.com@gmail.com)", "Wonsup Yoon (pusnow@kaist.ac.kr)", "Simon Jornet (github.com/jornetsimon)", "Chris Hendriks (github.com/olikilo)", "reddit.com/user/endallbeallknowitall", "Leonardo Bartoletti - leodmc88@gmail.com", "Will Pazner (github.com/pazner)", "Holton Jiang (github.com/holton-jiang)", "Vanessa C. (github.com/VChristinne)", "Max Akrman (github.com/isametry)", "Aagman (stscpns@gmail.com)", "Bünyamin Erol (bunyaminerol.com.tr)", "Wen Xiang (imwwx@icloud.com)" ] override func viewDidLoad() { if let dictionary = Bundle.main.infoDictionary, let ver = dictionary["CFBundleShortVersionString"] as? String, let build = dictionary["CFBundleVersion"] as? String { versionLabel.stringValue = "Version \(ver) (\(build))" versionLabel.isSelectable = true } translatorsList.delegate = self translatorsList.dataSource = self } @IBOutlet weak var versionLabel: NSTextField! @IBAction func openContributorsPage(_ sender: Any) { let url = URL(string: "https://github.com/glushchenko/fsnotes/graphs/contributors")! NSWorkspace.shared.open(url) } func numberOfRows(in tableView: NSTableView) -> Int { return languages.count } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { let result = tableView.makeView(withIdentifier: (tableColumn?.identifier)!, owner: self) as! NSTableCellView if tableColumn?.identifier.rawValue == "table.about.0" { result.textField?.stringValue = languages[row] } else { result.textField?.stringValue = authors[row] } return result } } ================================================ FILE: FSNotes/AboutWindowController.swift ================================================ // // AboutWindowController.swift // FSNotes // // Created by Олександр Глущенко on 5/10/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Cocoa class AboutWindowController: NSWindowController, NSWindowDelegate { override func windowDidLoad() { super.windowDidLoad() self.window?.delegate = self self.window?.title = "About" } } ================================================ FILE: FSNotes/AppDelegate+URLRoutes.swift ================================================ // // AppDelegate+URLRoutes.swift // FSNotes // // Created by Jeff Hanbury on 13/04/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation import Cocoa extension AppDelegate { enum HandledSchemes: String { case fsnotes = "fsnotes" case nv = "nv" case nvALT = "nvalt" case file = "file" } enum FSNotesRoutes: String { case find = "find" case new = "new" case open = "open" } enum NvALTRoutes: String { case find = "find" case blank = "" case make = "make" } func application(_ application: NSApplication, open urls: [URL]) { guard var url = urls.first, let scheme = url.scheme else { return } let path = url.absoluteString.escapePlus() if let escaped = URL(string: path) { url = escaped } switch scheme { case HandledSchemes.file.rawValue: if nil != ViewController.shared() { self.importNotes(urls: urls) } else { self.urls = urls } case HandledSchemes.fsnotes.rawValue: FSNotesRouter(url) case HandledSchemes.nv.rawValue, HandledSchemes.nvALT.rawValue: NvALTRouter(url) default: break } } func importNotes(urls: [URL]) { guard let vc = ViewController.shared() else { return } var importedNote: Note? = nil var sidebarIndex: Int? = nil for url in urls { if let items = vc.sidebarOutlineView.sidebarItems, let note = Storage.shared().getBy(url: url) { if let sidebarItem = items.first(where: { ($0 as? SidebarItem)?.project == note.project || $0 as? Project == note.project }) { sidebarIndex = vc.sidebarOutlineView.row(forItem: sidebarItem) importedNote = note } } else if let project = Storage.shared().getDefault() { let newUrl = vc.copy(project: project, url: url) UserDataService.instance.focusOnImport = newUrl UserDataService.instance.skipSidebarSelection = true } } if let note = importedNote, let si = sidebarIndex { vc.sidebarOutlineView.selectRowIndexes([si], byExtendingSelection: false) DispatchQueue.main.asyncAfter(deadline: .now() + 0.35, execute: { vc.notesTableView.setSelected(note: note) }) } } // MARK: - FSNotes routes func FSNotesRouter(_ url: URL) { guard let directive = url.host else { return } switch directive { case FSNotesRoutes.find.rawValue: RouteFSNotesFind(url) case FSNotesRoutes.new.rawValue: RouteFSNotesNew(url) case FSNotesRoutes.open.rawValue: RouteFSNotesOpen(url) default: break } } /// Handles URLs with the tag fsnotes://open/?tag=test /// Handles URLs with the tag fsnotes://open/?title=Open+Or+Create+If+Not+Exist /// func RouteFSNotesOpen(_ url: URL) { guard let vc = ViewController.shared() else { return } if let tag = url["tag"]?.removingPercentEncoding { vc.sidebarOutlineView.select(tag: tag) return } if let title = url["title"]?.removingPercentEncoding { if let note = Storage.shared().getBy(titleOrName: title) { if let txt = url["txt"] { // Append txt note.append(string: NSMutableAttributedString(string: txt + "\n\n")) _ = note.save() // Set last range note.setSelectedRange(range: NSRange(location: note.content.length, length: 0)) } // Reset UI and focus vc.cleanSearchAndEditArea(shouldBecomeFirstResponder: false, completion: { () -> Void in vc.notesTableView.selectRowAndSidebarItem(note: note) NSApp.mainWindow?.makeFirstResponder(vc.editor) vc.notesTableView.saveNavigationHistory(note: note) }) // Create NEW } else { RouteFSNotesNew(url) } } } /// Handles URLs with the path /find/searchstring1%20searchstring2 func RouteFSNotesFind(_ url: URL) { guard ViewController.shared() != nil else { self.url = url return } search(url: url) } public func search(url: URL) { guard let vc = ViewController.shared() else { return } var lastPath = url.lastPathComponent if let wikiURL = url["id"] { if let note = Storage.shared().getBy(titleOrName: wikiURL) { vc.cleanSearchAndEditArea(shouldBecomeFirstResponder: false, completion: { () -> Void in vc.notesTableView.selectRowAndSidebarItem(note: note) NSApp.mainWindow?.makeFirstResponder(vc.editor) vc.notesTableView.saveNavigationHistory(note: note) }) return } else { lastPath = wikiURL vc.search.window?.makeFirstResponder(vc.search) } } search(query: lastPath) } func search(query: String) { guard let controller = ViewController.shared() else { return } let searchQuery = SearchQuery() searchQuery.type = .All searchQuery.setFilter(query) controller.storage.setSearchQuery(value: searchQuery) controller.updateTable() { DispatchQueue.main.async { controller.search.stringValue = query if let note = controller.notesTableView.getNoteList().first { if note.title.lowercased() == query.lowercased() { controller.notesTableView.saveNavigationHistory(note: note) controller.notesTableView.setSelected(note: note) controller.view.window?.makeFirstResponder(controller.editor) } else { controller.search.suggestAutocomplete(note, filter: query) } } } } } /// Handles URLs with the following paths: /// - fsnotes://make/?title=URI-escaped-title&html=URI-escaped-HTML-data /// - fsnotes://make/?title=URI-escaped-title&txt=URI-escaped-plain-text /// - fsnotes://make/?txt=URI-escaped-plain-text /// /// The three possible parameters (title, txt, html) are all optional. /// func RouteFSNotesNew(_ url: URL) { let newWindow = url["open"] != nil let folderName = url["folder"] var title = "" var body = "" if let titleParam = url["title"] { title = titleParam } if let txtParam = url["txt"] { body = txtParam } else if let htmlParam = url["html"] { body = htmlParam } guard let vc = ViewController.shared() else { self.newName = title self.newContent = body self.newWindow = newWindow self.folderName = folderName return } guard let note = vc.createNote(name: title, content: body, folderName: folderName, openInNewWindow: newWindow), newWindow else { return } vc.openInNewWindow(note: note) } // MARK: - nvALT routes, for compatibility func NvALTRouter(_ url: URL) { guard let directive = url.host else { return } switch directive { case NvALTRoutes.find.rawValue: RouteNvAltFind(url) case NvALTRoutes.make.rawValue: RouteNvAltMake(url) default: RouteNvAltBlank(url) break } } /// Handle URLs in the format nv://find/searchstring1%20searchstring2 /// /// Note: this route is identical to the corresponding FSNotes route. /// func RouteNvAltFind(_ url: URL) { RouteFSNotesFind(url) } /// Handle URLs in the format nv://note%20title /// /// Note: this route is an alias to the /find route above. /// func RouteNvAltBlank(_ url: URL) { let pathWithFind = url.absoluteString.replacingOccurrences(of: "://", with: "://find/") guard let newURL = URL(string: pathWithFind) else { return } RouteFSNotesFind(newURL) } /// Handle URLs in the format: /// /// - nv://make/?title=URI-escaped-title&html=URI-escaped-HTML-data&tags=URI-escaped-tag-string /// - nv://make/?title=URI-escaped-title&txt=URI-escaped-plain-text /// - nv://make/?txt=URI-escaped-plain-text /// /// The four possible parameters (title, txt, html and tags) are all optional. /// func RouteNvAltMake(_ url: URL) { let newWindow = url["open"] != nil var title = "" var body = "" if let titleParam = url["title"] { title = titleParam } if let txtParam = url["txt"] { body = txtParam } else if let htmlParam = url["html"] { body = htmlParam } if let tagsParam = url["tags"] { body = body.appending("\n\nnvALT tags: \(tagsParam)") } guard let vc = ViewController.shared() else { self.newName = title self.newContent = body self.newWindow = newWindow return } guard let note = vc.createNote(name: title, content: body, openInNewWindow: newWindow), newWindow else { return } vc.openInNewWindow(note: note) } } ================================================ FILE: FSNotes/AppDelegate.swift ================================================ // // AppDelegate.swift // FSNotes // // Created by Oleksandr Glushchenko on 7/20/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Cocoa import UserNotifications @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate { var prefsWindowController: PrefsWindowController? var aboutWindowController: AboutWindowController? var statusItem: NSStatusItem? public var urls: [URL]? = nil public var url: URL? = nil public var newName: String? = nil public var newContent: String? = nil public var folderName: String? = nil public var newWindow: Bool = false public static var mainWindowController: MainWindowController? public static var noteWindows = [NSWindowController]() public static var appTitle: String { let name = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String return name ?? Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String) as! String } public static var gitProgress: GitProgress? func applicationWillFinishLaunching(_ notification: Notification) { checkStorageChanges() loadDockIcon() if UserDefaultsManagement.showInMenuBar { constructMenu() } if !UserDefaultsManagement.showDockIcon { let transformState = ProcessApplicationTransformState(kProcessTransformToUIElementApplication) var psn = ProcessSerialNumber(highLongOfPSN: 0, lowLongOfPSN: UInt32(kCurrentProcess)) TransformProcessType(&psn, transformState) NSApp.setActivationPolicy(.accessory) } } func applicationDidFinishLaunching(_ aNotification: Notification) { // Ensure the font panel is closed when the app starts, in case it was // left open when the app quit. NSFontManager.shared.fontPanel(false)?.orderOut(self) applyAppearance() #if CLOUD_RELATED_BLOCK if let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents").standardized { if (!FileManager.default.fileExists(atPath: iCloudDocumentsURL.path, isDirectory: nil)) { do { try FileManager.default.createDirectory(at: iCloudDocumentsURL, withIntermediateDirectories: true, attributes: nil) } catch { print("Home directory creation: \(error)") } } } #endif if UserDefaultsManagement.storagePath == nil { self.requestStorageDirectory() return } let storyboard = NSStoryboard(name: "Main", bundle: nil) guard let mainWC = storyboard.instantiateController(withIdentifier: "MainWindowController") as? MainWindowController else { fatalError("Error getting main window controller") } AppDelegate.mainWindowController = mainWC mainWC.window?.makeKeyAndOrderFront(nil) } func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool { if (!flag) { AppDelegate.mainWindowController?.makeNew() } return true } func applicationWillTerminate(_ notification: Notification) { UserDefaultsManagement.crashedLastTime = false AppDelegate.saveWindowsState() Storage.shared().saveUploadPaths() let webkitPreview = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("wkPreview") try? FileManager.default.removeItem(at: webkitPreview) let printDir = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("Print") try? FileManager.default.removeItem(at: printDir) let encryption = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("Encryption") try? FileManager.default.removeItem(at: encryption) var temporary = URL(fileURLWithPath: NSTemporaryDirectory()) temporary.appendPathComponent("ThumbnailsBig") try? FileManager.default.removeItem(at: temporary) let storyboard = NSStoryboard(name: "Main", bundle: nil) guard let mainWC = storyboard.instantiateController(withIdentifier: "MainWindowController") as? MainWindowController else { return } if let x = mainWC.window?.frame.origin.x, let y = mainWC.window?.frame.origin.y { UserDefaultsManagement.lastScreenX = Int(x) UserDefaultsManagement.lastScreenY = Int(y) } Storage.shared().saveProjectsCache() print("Termination end, crash status: \(UserDefaultsManagement.crashedLastTime)") } private static func saveWindowsState() { var result = [[String: Any]]() let noteWindows = self.noteWindows.sorted(by: { $0.window!.orderedIndex > $1.window!.orderedIndex }) for windowController in noteWindows { if let frame = windowController.window?.frame, let data = try? NSKeyedArchiver.archivedData(withRootObject: frame, requiringSecureCoding: true), let controller = windowController.contentViewController as? NoteViewController, let note = controller.editor.note { let key = windowController.window?.isKeyWindow == true result.append(["frame": data, "preview": controller.editor.isPreviewEnabled(), "url": note.url, "main": false, "key": key]) } } // Main frame if let vc = ViewController.shared(), let note = vc.editor?.note, let mainFrame = vc.view.window?.frame, let data = try? NSKeyedArchiver.archivedData(withRootObject: mainFrame, requiringSecureCoding: true) { let key = vc.view.window?.isKeyWindow == true result.append(["frame": data, "preview": vc.editor.isPreviewEnabled(), "url": note.url, "main": true, "key": key]) } let projectsData = try? NSKeyedArchiver.archivedData(withRootObject: result, requiringSecureCoding: true) if let documentDir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first { try? projectsData?.write(to: documentDir.appendingPathComponent("editors.settings")) } } private func applyAppearance() { if UserDefaultsManagement.appearanceType == .Dark { NSApp.appearance = NSAppearance.init(named: NSAppearance.Name.darkAqua) UserDataService.instance.isDark = true } if UserDefaultsManagement.appearanceType == .Light { NSApp.appearance = NSAppearance.init(named: NSAppearance.Name.aqua) UserDataService.instance.isDark = false } if UserDefaultsManagement.appearanceType == .System, NSAppearance.current.isDark { UserDataService.instance.isDark = true } } private func restartApp() { guard let resourcePath = Bundle.main.resourcePath else { return } let url = URL(fileURLWithPath: resourcePath) let path = url.deletingLastPathComponent().deletingLastPathComponent().absoluteString let task = Process() task.launchPath = "/usr/bin/open" task.arguments = [path] task.launch() exit(0) } private func requestStorageDirectory() { var directoryURL: URL? = nil if let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first { directoryURL = URL(fileURLWithPath: path) } let panel = NSOpenPanel() panel.directoryURL = directoryURL panel.allowsMultipleSelection = false panel.canChooseDirectories = true panel.canChooseFiles = false panel.canCreateDirectories = true panel.message = "Please select default storage directory" panel.begin { (result) -> Void in if result == .OK { guard let url = panel.url else { return } let bookmarks = SandboxBookmark.sharedInstance() bookmarks.save(url: url) UserDefaultsManagement.storageType = .custom UserDefaultsManagement.customStoragePath = url.path self.restartApp() } else { exit(EXIT_SUCCESS) } } } func constructMenu() { statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) if let button = statusItem?.button, let image = NSImage(named: "menuBar") { image.size.width = 20 image.size.height = 20 button.image = image } statusItem?.button?.action = #selector(AppDelegate.clickStatusBarItem(sender:)) statusItem?.button?.sendAction(on: [.leftMouseUp, .rightMouseUp]) } public func attachMenu() { let menu = NSMenu() menu.addItem(NSMenuItem(title: NSLocalizedString("New Note", comment: ""), action: #selector(AppDelegate.new(_:)), keyEquivalent: "n")) let newWindow = NSMenuItem(title: NSLocalizedString("New Note in New Window", comment: ""), action: #selector(AppDelegate.createInNewWindow(_:)), keyEquivalent: "n") var modifier = NSEvent.modifierFlags modifier.insert(.command) modifier.insert(.shift) newWindow.keyEquivalentModifierMask = modifier menu.addItem(newWindow) menu.addItem(NSMenuItem.separator()) menu.addItem(NSMenuItem(title: NSLocalizedString("Search and create", comment: ""), action: #selector(AppDelegate.searchAndCreate(_:)), keyEquivalent: "l")) menu.addItem(NSMenuItem(title: NSLocalizedString("Settings", comment: ""), action: #selector(AppDelegate.openPreferences(_:)), keyEquivalent: ",")) let lock = NSMenuItem(title: NSLocalizedString("Lock All Encrypted", comment: ""), action: #selector(ViewController.shared()?.lockAll(_:)), keyEquivalent: "l") lock.keyEquivalentModifierMask = [.command, .shift] menu.addItem(lock) menu.addItem(NSMenuItem.separator()) menu.addItem(NSMenuItem(title: NSLocalizedString("Quit FSNotes", comment: ""), action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")) menu.delegate = self statusItem?.menu = menu } @objc func clickStatusBarItem(sender: NSStatusItem) { let event = NSApp.currentEvent! if event.type == NSEvent.EventType.leftMouseUp { // Hide active not hidden and not miniaturized if !NSApp.isHidden && NSApp.isActive { if let mainWindow = AppDelegate.mainWindowController?.window, !mainWindow.isMiniaturized { NSApp.hide(nil) return } } NSApp.unhide(nil) NSApp.activate(ignoringOtherApps: true) AppDelegate.mainWindowController?.window?.makeKeyAndOrderFront(nil) ViewController.shared()?.search.becomeFirstResponder() return } attachMenu() DispatchQueue.main.async { if let statusItem = self.statusItem, let button = statusItem.button { statusItem.menu?.popUp(positioning: nil, at: NSPoint(x: button.frame.origin.x, y: button.frame.height + 10), in: button) } } } func menuDidClose(_ menu: NSMenu) { statusItem?.menu = nil } // MARK: IBActions @IBAction func openMainWindow(_ sender: Any) { AppDelegate.mainWindowController?.makeNew() } @IBAction func openHelp(_ sender: Any) { NSWorkspace.shared.open(URL(string: "https://github.com/glushchenko/fsnotes/wiki")!) } @IBAction func openReportsAndRequests(_ sender: Any) { NSWorkspace.shared.open(URL(string: "https://github.com/glushchenko/fsnotes/issues/new/choose")!) } @IBAction func openSite(_ sender: Any) { NSWorkspace.shared.open(URL(string: "https://fsnot.es")!) } @IBAction func openPreferences(_ sender: Any?) { if prefsWindowController == nil { let storyboard = NSStoryboard(name: "Main", bundle: nil) prefsWindowController = storyboard.instantiateController(withIdentifier: "Preferences") as? PrefsWindowController } guard let prefsWindowController = prefsWindowController else { return } prefsWindowController.showWindow(nil) prefsWindowController.window?.makeKeyAndOrderFront(prefsWindowController) NSApp.activate(ignoringOtherApps: true) } @IBAction func new(_ sender: Any?) { AppDelegate.mainWindowController?.makeNew() NSApp.activate(ignoringOtherApps: true) ViewController.shared()?.fileMenuNewNote(self) } @IBAction func createInNewWindow(_ sender: Any?) { AppDelegate.mainWindowController?.makeNew() NSApp.activate(ignoringOtherApps: true) ViewController.shared()?.createInNewWindow(self) } @IBAction func searchAndCreate(_ sender: Any?) { AppDelegate.mainWindowController?.makeNew() NSApp.activate(ignoringOtherApps: true) guard let vc = ViewController.shared() else { return } DispatchQueue.main.async { vc.search.window?.makeFirstResponder(vc.search) } } @IBAction func removeMenuBar(_ sender: Any?) { guard let statusItem = statusItem else { return } NSStatusBar.system.removeStatusItem(statusItem) } @IBAction func addMenuBar(_ sender: Any?) { constructMenu() } @IBAction func showAboutWindow(_ sender: AnyObject) { if aboutWindowController == nil { let storyboard = NSStoryboard(name: "Main", bundle: nil) aboutWindowController = storyboard.instantiateController(withIdentifier: "About") as? AboutWindowController } guard let aboutWindowController = aboutWindowController else { return } aboutWindowController.showWindow(nil) aboutWindowController.window?.makeKeyAndOrderFront(aboutWindowController) NSApp.activate(ignoringOtherApps: true) } public func loadDockIcon() { var image: Image? switch UserDefaultsManagement.dockIcon { case 0: image = NSImage(named: "modern") break case 1: image = NSImage(named: "AppIconClassic") break default: break } guard let im = image else { return } let appDockTile = NSApplication.shared.dockTile if #available(OSX 10.12, *) { appDockTile.contentView = NSImageView(image: im) } appDockTile.display() } private func checkStorageChanges() { if Storage.shared().shouldMovePrompt, let local = UserDefaultsManagement.localDocumentsContainer, let iCloudDrive = UserDefaultsManagement.iCloudDocumentsContainer { let message = NSLocalizedString("We are detect that you are install FSNotes from Mac App Store with default storage in iCloud Drive, do you want to move old database in iCloud Drive?", comment: "") promptToMoveDatabase(from: local, to: iCloudDrive, messageText: message) } } public func promptToMoveDatabase(from currentURL: URL, to url : URL, messageText: String) { let alert = NSAlert() alert.messageText = messageText alert.informativeText = NSLocalizedString("Otherwise, the database of your notes will be available at: ", comment: "") + currentURL.path alert.alertStyle = .warning alert.addButton(withTitle: NSLocalizedString("No", comment: "")) alert.addButton(withTitle: NSLocalizedString("Yes", comment: "")) if alert.runModal() == .alertSecondButtonReturn { move(from: currentURL, to: url) let localTrash = currentURL.appendingPathComponent("Trash", isDirectory: true) let cloudTrash = url.appendingPathComponent("Trash", isDirectory: true) move(from: localTrash, to: cloudTrash) } } private func move(from currentURL: URL, to url: URL) { if let list = try? FileManager.default.contentsOfDirectory(at: currentURL, includingPropertiesForKeys: nil, options: .init()) { if !FileManager.default.fileExists(atPath: currentURL.path) { return } if !FileManager.default.fileExists(atPath: url.path) { try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil) } for item in list { let fileName = item.lastPathComponent do { let dst = url.appendingPathComponent(fileName) try FileManager.default.moveItem(at: item, to: dst) } catch { if ["Trash", "Welcome"].contains(fileName) { continue } let exist = NSAlert() var message = NSLocalizedString("We can not move \"{DST_PATH}\" because this item already exist in selected destination.", comment: "") message = message.replacingOccurrences(of: "{DST_PATH}", with: item.path) exist.messageText = message exist.addButton(withTitle: NSLocalizedString("OK", comment: "")) exist.runModal() } } } } func application(_ application: NSApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([NSUserActivityRestoring]) -> Void) -> Bool { ViewController.shared()?.restoreUserActivityState(userActivity) return true } func application(_ application: NSApplication, willContinueUserActivityWithType userActivityType: String) -> Bool { return true } func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { return true } public static func getEditTextViews() -> [EditTextView] { var views = getOpenedEditTextViews() if let controller = mainWindowController?.contentViewController as? ViewController { views.append(controller.editor) } return views } public static func getOpenedEditTextViews() -> [EditTextView] { var views = [EditTextView]() for window in noteWindows { if let controller = window.contentViewController as? NoteViewController { views.append(controller.editor) } } return views } } ================================================ FILE: FSNotes/Base.lproj/Main.storyboard ================================================ CA CA CA NSAllRomanInputSourcesLocaleIdentifier NSAllRomanInputSourcesLocaleIdentifier NSAllRomanInputSourcesLocaleIdentifier Copyright © 2017-2024 Oleksandr Hlushchenko. All rights reserved. Dylan Seeger — https://www.lovably.com Olena Hlushcneko NSAllRomanInputSourcesLocaleIdentifier NSAllRomanInputSourcesLocaleIdentifier NSAllRomanInputSourcesLocaleIdentifier NSAllRomanInputSourcesLocaleIdentifier ================================================ FILE: FSNotes/EditorViewController+ScrollPosition.swift ================================================ // // EditorViewController+ScrollPosition.swift // FSNotes // // Created by Oleksandr Hlushchenko on 22.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import Foundation import AppKit extension EditorViewController { func initScrollObserver() { if let textView = vcEditor, let scrollView = textView.enclosingScrollView { NotificationCenter.default.addObserver( self, selector: #selector(scrollViewDidScroll), name: NSView.boundsDidChangeNotification, object: scrollView.contentView ) scrollView.contentView.postsBoundsChangedNotifications = true } } func restoreScrollPosition() { guard let textView = vcEditor, let charIndex = textView.note?.scrollPosition, let layoutManager = textView.layoutManager, let textContainer = textView.textContainer else { vcEditor?.isScrollPositionSaverLocked = false return } layoutManager.ensureLayout(for: textContainer) let glyphIndex = layoutManager.glyphIndexForCharacter(at: charIndex) let rect = layoutManager.boundingRect(forGlyphRange: NSRange(location: glyphIndex, length: 1), in: textContainer) textView.scroll(rect.origin) textView.isScrollPositionSaverLocked = false } @objc func scrollViewDidScroll(_ notification: Notification) { guard notification.object as? NSClipView != nil else { return } if let textView = vcEditor, !textView.isPreviewEnabled(), !textView.isScrollPositionSaverLocked { guard let layoutManager = textView.layoutManager, let textContainer = textView.textContainer else { return } let visibleRect = textView.enclosingScrollView!.contentView.bounds let glyphRange = layoutManager.glyphRange(forBoundingRect: visibleRect, in: textContainer) textView.note?.scrollPosition = layoutManager.characterIndexForGlyph(at: glyphRange.location) } } } ================================================ FILE: FSNotes/EditorViewController+Sharing.swift ================================================ // // EditorViewController+Sharing.swift // FSNotes // // Created by Oleksandr Hlushchenko on 03.07.2022. // Copyright © 2022 Oleksandr Hlushchenko. All rights reserved. // import Cocoa extension EditorViewController: NSSharingServicePickerDelegate { func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, sharingServicesForItems items: [Any], proposedSharingServices proposedServices: [NSSharingService]) -> [NSSharingService] { var share = proposedServices if #available(macOS 11.0, *) { guard let image = NSImage(systemSymbolName: "document.on.document", accessibilityDescription: nil), let webImage = NSImage(named: "web") else { return proposedServices } let titleWeb = NSLocalizedString("Web", comment: "") let web = NSSharingService(title: titleWeb, image: webImage, alternateImage: nil, handler: { ViewController.shared()?.uploadWebNote(NSMenuItem()) }) share.insert(web, at: 0) let titlePlain = NSLocalizedString("Copy Plain Text", comment: "") let plainText = NSSharingService(title: titlePlain, image: image, alternateImage: image, handler: { self.saveTextAtClipboard() }) share.insert(plainText, at: 1) let titleHTML = NSLocalizedString("Copy HTML", comment: "") let html = NSSharingService(title: titleHTML, image: image, alternateImage: image, handler: { self.saveHtmlAtClipboard() }) share.insert(html, at: 2) } return share } //MARK: Share Service public func saveTextAtClipboard() { if let note = vcEditor?.note { let unloadedText = note.content.unloadTasks() let pasteboard = NSPasteboard.general pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) pasteboard.setString(unloadedText.string, forType: NSPasteboard.PasteboardType.string) } } public func saveHtmlAtClipboard() { if let note = vcEditor?.note { let unloadedText = note.content.unloadTasks() if let render = renderMarkdownHTML(markdown: unloadedText.string) { let pasteboard = NSPasteboard.general pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) pasteboard.setString(render, forType: NSPasteboard.PasteboardType.string) } } } } ================================================ FILE: FSNotes/EditorViewController.swift ================================================ // // EditorViewController.swift // FSNotes // // Created by Oleksandr Hlushchenko on 26.06.2022. // Copyright © 2022 Oleksandr Hlushchenko. All rights reserved. // import Foundation import AppKit import LocalAuthentication import WebKit import UserNotifications class EditorViewController: NSViewController, NSTextViewDelegate, NSMenuItemValidation { public var alert: NSAlert? public var noteLoading: ProgressState = .none public var vcEditor: EditTextView? public var vcTitleLabel: TitleTextField? public var vcNonSelectedLabel: NSTextField? public var vcPreviewButton: NSButton? public var vcShareButton: NSButton? public var vcLockUnlockButton: NSButton? public var vcEditorScrollView: EditorScrollView? public var previewResizeTimer = Timer() public var rowUpdaterTimer = Timer() public var editorUndoManager = UndoManager() public var breakUndoTimer = Timer() // git public var snapshotsTimer = Timer() public var lastSnapshot: Int? public var pullTimer = Timer() public var encPassword: NSSecureTextField? public var encVerifyPassword: NSSecureTextField? public var encCompletionHandler: ((String) -> Void)? public func initView() { guard let editor = vcEditor else { return } editor.delegate = self initScrollObserver() editor.isGrammarCheckingEnabled = UserDefaultsManagement.grammarChecking editor.isContinuousSpellCheckingEnabled = UserDefaultsManagement.continuousSpellChecking editor.smartInsertDeleteEnabled = UserDefaultsManagement.smartInsertDelete editor.isAutomaticSpellingCorrectionEnabled = UserDefaultsManagement.automaticSpellingCorrection editor.isAutomaticQuoteSubstitutionEnabled = UserDefaultsManagement.automaticQuoteSubstitution editor.isAutomaticDataDetectionEnabled = UserDefaultsManagement.automaticDataDetection editor.isAutomaticLinkDetectionEnabled = UserDefaultsManagement.automaticLinkDetection editor.isAutomaticTextReplacementEnabled = UserDefaultsManagement.automaticTextReplacement editor.isAutomaticDashSubstitutionEnabled = UserDefaultsManagement.automaticDashSubstitution } deinit { NotificationCenter.default.removeObserver(self) } func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { guard let vc = ViewController.shared() else { return false} // Current note var note = vc.editor.note if note == nil { note = vc.getSelectedNotes()?.first } let ident = menuItem.identifier?.rawValue if let title = menuItem.menu?.identifier?.rawValue { switch title { case "fsnotesMenu": if menuItem.identifier?.rawValue == "fsnotes.emptyBin" { menuItem.keyEquivalentModifierMask = UserDefaultsManagement.focusInEditorOnNoteSelect ? [.command, .option, .shift] : [.command, .shift] menuItem.title = NSLocalizedString("Empty Bin", comment: "") return true } case "fileMenu": return vc.processFileMenuItems(menuItem, menuId: title) case "shareMenu": return vc.processShareMenuItems(menuItem, menuId: title) case "folderMenu": return vc.processLibraryMenuItems(menuItem, menuId: title) case "findMenu": guard let evc = NSApplication.shared.keyWindow?.contentViewController as? EditorViewController, evc.vcEditor?.note != nil else { return false } if evc.vcEditor?.markdownView == nil { if ["findMenu.find", "findMenu.findAndReplace", "findMenu.next", "findMenu.prev", "findMenu.selectionToFind" ].contains(menuItem.identifier?.rawValue) { return true } } else { if ["findMenu.find", "findMenu.next", "findMenu.prev", "findMenu.selectionToFind" ].contains(menuItem.identifier?.rawValue) { return true } } return false case "viewSortBy": let iconName = UserDefaultsManagement.sortDirection ? "arrow.down" : "arrow.up" switch menuItem.tag { case 1: if UserDefaultsManagement.sort == .modificationDate { if #available(macOS 11.0, *) { menuItem.image = NSImage.init(systemSymbolName: iconName, accessibilityDescription: nil) menuItem.state = .off } else { menuItem.state = .on menuItem.image = NSImage() } } else { menuItem.state = .off menuItem.image = NSImage() } case 2: if UserDefaultsManagement.sort == .creationDate { if #available(macOS 11.0, *) { menuItem.image = NSImage.init(systemSymbolName: iconName, accessibilityDescription: nil) menuItem.state = .off } else { menuItem.state = .on menuItem.image = NSImage() } } else { menuItem.state = .off menuItem.image = NSImage() } case 3: if UserDefaultsManagement.sort == .title { if #available(macOS 11.0, *) { menuItem.image = NSImage.init(systemSymbolName: iconName, accessibilityDescription: nil) menuItem.state = .off } else { menuItem.state = .on menuItem.image = NSImage() } } else { menuItem.state = .off menuItem.image = NSImage() } default: break } case "showInSidebar": switch menuItem.tag { case 1: menuItem.state = UserDefaultsManagement.sidebarVisibilityInbox ? .on : .off case 2: menuItem.state = UserDefaultsManagement.sidebarVisibilityNotes ? .on : .off case 3: menuItem.state = UserDefaultsManagement.sidebarVisibilityTodo ? .on : .off case 5: menuItem.state = UserDefaultsManagement.sidebarVisibilityTrash ? .on : .off case 6: menuItem.state = UserDefaultsManagement.sidebarVisibilityUntagged ? .on : .off default: break } case "viewMenu": switch ident { case "previewMathJax": menuItem.state = UserDefaultsManagement.mathJaxPreview ? .on : .off break case "viewMenu.historyBack": if vc.notesTableView.historyPosition == 0 { return false } break case "viewMenu.historyForward": if vc.notesTableView.historyPosition == vc.notesTableView.history.count - 1 { return false } break case "view.toggleNoteList": menuItem.title = vc.isVisibleNoteList() ? NSLocalizedString("Hide Note List", comment: "") : NSLocalizedString("Show Note List", comment: "") break case "view.toggleSidebar": menuItem.title = vc.isVisibleSidebar() ? NSLocalizedString("Hide Sidebar", comment: "") : NSLocalizedString("Show Sidebar", comment: "") break case "viewMenu.actualSize": return UserDefaultsManagement.fontSize != UserDefaultsManagement.DefaultFontSize default: break } default: break } } return true } public func getSelectedNotes() -> [Note]? { // Opened window if NSApplication.shared.keyWindow?.contentViewController?.isKind(of: NoteViewController.self) == true, let evc = NSApplication.shared.keyWindow?.contentViewController as? EditorViewController, let note = evc.vcEditor?.note { return [note] } // Active main window if let cvc = NSApplication.shared.keyWindow?.contentViewController, cvc.isKind(of: ViewController.self), let vc = ViewController.shared(), let selected = vc.notesTableView.getSelectedNotes() { return selected } return nil } public func getSelectedNote() -> Note? { // Opened window if NSApplication.shared.keyWindow?.contentViewController?.isKind(of: NoteViewController.self) == true, let evc = NSApplication.shared.keyWindow?.contentViewController as? EditorViewController, let note = evc.vcEditor?.note { return note } // Active main window if let cvc = NSApplication.shared.keyWindow?.contentViewController, cvc.isKind(of: ViewController.self), let vc = ViewController.shared(), let selected = vc.notesTableView.getSelectedNotes()?.first { return selected } return nil } private func isFirstResponder(responder: AnyClass) -> Bool { return view.window?.firstResponder?.isKind(of: responder) == true } private func isOpenedInNewWindow() -> Bool { return NSApplication.shared.keyWindow?.contentViewController?.isKind(of: NoteViewController.self) == true } // MARK: Window bar actions @IBAction func textFinder(_ sender: NSMenuItem) { guard let evc = NSApplication.shared.keyWindow?.contentViewController as? EditorViewController, evc.vcEditor?.note != nil else { return } if let mView = evc.vcEditor?.markdownView { mView.performTextFinderAction(sender) return } if let editView = evc.vcEditor { editView.performFindPanelAction(sender) } } @IBAction func fsToggleLockItem(_ sender: NSMenuItem) { guard let vc = ViewController.shared() else { return } if isFirstResponder(responder: SidebarOutlineView.self) { vc.sidebarOutlineView.toggleFolderLock(sender) return } if isFirstResponder(responder: NotesTableView.self) || isFirstResponder(responder: EditTextView.self) || isOpenedInNewWindow() { vc.toggleNotesLock(sender) return } } @IBAction func fsDecryptItem(_ sender: NSMenuItem) { guard let vc = ViewController.shared() else { return } if isFirstResponder(responder: SidebarOutlineView.self) { vc.sidebarOutlineView.removeFolderEncryption(sender) return } if isFirstResponder(responder: NotesTableView.self) || isOpenedInNewWindow() { vc.removeNoteEncryption(sender) return } } @IBAction func fsRevealItem(_ sender: NSMenuItem) { guard let vc = ViewController.shared() else { return } if isFirstResponder(responder: SidebarOutlineView.self) { vc.sidebarOutlineView.revealInFinder(sender) return } if isFirstResponder(responder: NotesTableView.self) || isFirstResponder(responder: EditTextView.self) || isOpenedInNewWindow() { vc.finderMenu(sender) return } } @IBAction func fsRenameItem(_ sender: NSMenuItem) { guard let vc = ViewController.shared() else { return } if isFirstResponder(responder: SidebarOutlineView.self) || isOpenedInNewWindow() { vc.sidebarOutlineView.renameFolderMenu(sender) return } if isFirstResponder(responder: NotesTableView.self) || isFirstResponder(responder: EditTextView.self) { vc.renameMenu(sender) return } } @IBAction func toggleNotesLock(_ sender: Any) { guard let vc = ViewController.shared(), let evc = NSApplication.shared.keyWindow?.contentViewController as? EditorViewController else { return } let isOpenedWindow = NSApplication.shared.keyWindow?.contentViewController?.isKind(of: NoteViewController.self) == true var notes = vc.getSelectedNotes() if isOpenedWindow, let note = evc.vcEditor?.note { notes = [note] } guard let first = notes?.first, let notes = notes else { return } // Lock unlocked if first.isUnlocked() { _ = lockUnlocked(notes: notes) return } // Unlock encrypted if first.container == .encryptedTextPack { getMasterPassword() { password in guard password.count > 0 else { return } for note in notes { guard note.isEncryptedAndLocked(), note.unLock(password: password) else { continue } let insertTags = note.scanContentTags().0 DispatchQueue.main.async { self.reloadAllOpenedWindows(note: note) ViewController.shared()?.sidebarOutlineView?.addTags(insertTags) ViewController.shared()?.notesTableView.reloadRow(note: note) } } } return } // Encrypt plain getMasterPassword(forEncrypt: true) { password in for note in notes { if !note.isEncrypted(), note.encrypt(password: password) { note.password = nil DispatchQueue.main.async { self.reloadAllOpenedWindows(note: note) ViewController.shared()?.focusTable() ViewController.shared()?.notesTableView.reloadRow(note: note) } } } } } @IBAction func openProjectViewSettings(_ sender: NSMenuItem) { guard let vc = ViewController.shared() else { return } if let controller = vc.storyboard?.instantiateController(withIdentifier: "ProjectSettingsViewController") as? ProjectSettingsViewController { vc.projectSettingsViewController = controller if let project = vc.sidebarOutlineView.getSelectedProject() { vc.presentAsSheet(controller) controller.load(project: project) } } } @IBAction func createFolder(_ sender: Any) { guard let vc = ViewController.shared(), let sidebarOutlineView = vc.sidebarOutlineView else { return } // Call from menu bar if let sender = sender as? NSMenuItem, sender.identifier?.rawValue == "fileMenu.attach" { sidebarOutlineView.addRoot() return } // Call from popup menu or menu bar var project = sidebarOutlineView.getSelectedProject() if project == nil || project?.isVirtual == true || !isFirstResponder(responder: SidebarOutlineView.self) { project = Storage.shared().getDefault() } guard let project = project, let window = MainWindowController.shared() else { return } let alert = NSAlert() vc.alert = alert let field = NSTextField(frame: NSRect(x: 0, y: 0, width: 290, height: 20)) alert.messageText = NSLocalizedString("New project", comment: "") alert.informativeText = NSLocalizedString("Please enter project name:", comment: "") alert.accessoryView = field alert.alertStyle = .informational alert.addButton(withTitle: NSLocalizedString("Add", comment: "")) alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) alert.beginSheetModal(for: window) { (returnCode: NSApplication.ModalResponse) -> Void in if returnCode == NSApplication.ModalResponse.alertFirstButtonReturn { let name = field.stringValue guard name.count > 0 else { return } OperationQueue.main.addOperation { vc.sidebarOutlineView.createProject(in: project, with: name) } } NSApp.mainWindow?.makeFirstResponder(sidebarOutlineView) vc.alert = nil } field.becomeFirstResponder() } @IBAction func togglePreview(_ sender: Any) { guard let editor = vcEditor else { return } let firstResp = view.window?.firstResponder editor.togglePreviewState() if (editor.isPreviewEnabled()) { //Preview mode doesn't support text search cancelTextSearch() refillEditArea(force: true) if let mdView = vcEditor?.editorViewController?.vcEditor?.markdownView { view.window?.makeFirstResponder(mdView) } } else { disablePreview() } if let responder = firstResp, ( ViewController.shared()?.search.currentEditor() == firstResp || responder.isKind(of: NotesTableView.self) || responder.isKind(of: SidebarOutlineView.self) ) { view.window?.makeFirstResponder(firstResp) } else { var responder: NSResponder? = vcEditor if vcEditor?.isPreviewEnabled() == true, let mView = vcEditor?.markdownView { responder = mView } if let responder = responder { view.window?.makeFirstResponder(responder) } } vcEditor?.userActivity?.needsSave = true editor.note?.project.saveNotesPreview() } @IBAction func toggleMathJax(_ sender: NSMenuItem) { sender.state = sender.state == .on ? .off : .on UserDefaultsManagement.mathJaxPreview = sender.state == .on refillEditArea(force: true) } @IBAction func shareSheet(_ sender: NSButton) { if let note = vcEditor?.note { let sharingPicker = NSSharingServicePicker(items: [ note.content, note.url ]) sharingPicker.delegate = self sharingPicker.show(relativeTo: NSZeroRect, of: sender, preferredEdge: .minY) } } // MARK: File menu @IBAction func printNotes(_ sender: NSMenuItem) { guard let notes = getSelectedNotes(), let note = notes.first else { return } if note.isMarkdown() { printMarkdownPreview() return } let pv = NSTextView(frame: NSMakeRect(0, 0, 528, 688)) pv.textStorage?.append(note.content) let printInfo = NSPrintInfo.shared printInfo.isHorizontallyCentered = false printInfo.isVerticallyCentered = false printInfo.scalingFactor = 1 printInfo.topMargin = 40 printInfo.leftMargin = 40 printInfo.rightMargin = 40 printInfo.bottomMargin = 40 let operation: NSPrintOperation = NSPrintOperation(view: pv, printInfo: printInfo) operation.printPanel.options.insert(NSPrintPanel.Options.showsPaperSize) operation.printPanel.options.insert(NSPrintPanel.Options.showsOrientation) operation.run() } @IBAction func finderMenu(_ sender: NSMenuItem) { guard let notes = getSelectedNotes() else { return } var urls = [URL]() for note in notes { urls.append(note.url) } NSWorkspace.shared.activateFileViewerSelecting(urls) } @IBAction func pinMenu(_ sender: Any) { guard let notes = getSelectedNotes() else { return } ViewController.shared()?.pin(selectedNotes: notes, toggle: true) } @IBAction func editorMenu(_ sender: Any) { guard let notes = getSelectedNotes() else { return } ViewController.shared()?.external(selectedNotes: notes) } @IBAction func copyURL(_ sender: Any) { guard let note = getSelectedNotes()?.first else { return } if let title = note.title.addingPercentEncoding(withAllowedCharacters: .alphanumerics) { let name = "fsnotes://find?id=\(title)" let pasteboard = NSPasteboard.general pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) pasteboard.setString(name, forType: NSPasteboard.PasteboardType.string) UNUserNotificationCenter.current().getNotificationSettings { settings in guard settings.authorizationStatus == .notDetermined else { return } UNUserNotificationCenter.current().requestAuthorization( options: [.alert, .sound] ) { _, _ in } } let content = UNMutableNotificationContent() content.title = NSLocalizedString("URL has been copied to clipboard", comment: "") content.body = name content.sound = .default UNUserNotificationCenter.current().add( UNNotificationRequest( identifier: UUID().uuidString, content: content, trigger: nil )) } } @IBAction func copyTitle(_ sender: Any) { guard let note = getSelectedNotes()?.first else { return } let pasteboard = NSPasteboard.general pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) pasteboard.setString(note.title, forType: NSPasteboard.PasteboardType.string) } @IBAction func removeNoteEncryption(_ sender: Any) { guard var notes = getSelectedNotes(), let vc = ViewController.shared() else { return } notes = decryptUnlocked(notes: notes) guard notes.count > 0 else { return } getMasterPassword() { password in for note in notes { if note.container == .encryptedTextPack { let success = note.unEncrypt(password: password) if success && notes.count == 0x01 { note.password = nil DispatchQueue.main.async { self.reloadAllOpenedWindows(note: note) } } } vc.notesTableView.reloadRow(note: note) } } } @IBAction func changeCreationDate(_ sender: Any) { guard let notes = getSelectedNotes() else { return } guard let note = notes.first else { return } guard let creationDate = note.getFileCreationDate() else { return } guard let window = view.window else { return } alert = NSAlert() let field = NSTextField(frame: NSRect(x: 0, y: 0, width: 290, height: 20)) let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" let date = formatter.string(from: creationDate) field.stringValue = date field.placeholderString = "2020-08-28 21:59:07" alert?.messageText = NSLocalizedString("Change Creation Date", comment: "Menu") + ":" alert?.accessoryView = field alert?.alertStyle = .informational alert?.addButton(withTitle: "OK") alert?.beginSheetModal(for: window) { (returnCode: NSApplication.ModalResponse) -> Void in if returnCode == NSApplication.ModalResponse.alertFirstButtonReturn { for note in notes { if note.setCreationDate(string: field.stringValue) { ViewController.shared()?.notesTableView.reloadRow(note: note) } } } self.alert = nil } field.becomeFirstResponder() } @IBAction func createInNewWindow(_ sender: Any) { var content = String() if let inlineTags = ViewController.shared()?.sidebarOutlineView.getSelectedInlineTags() { content = inlineTags } if let note = createNote(content: content, openInNewWindow: true) { openInNewWindow(note: note) } } @IBAction func quickNote(_ sender: Any) { if let note = createNote(content: "", openInNewWindow: true) { NSApp.activate(ignoringOtherApps: true) if !NSApp.isActive { AppDelegate.mainWindowController?.window?.miniaturize(self) } openInNewWindow(note: note) } } @IBAction func historyMenu(_ sender: Any) { guard let cvc = NSApplication.shared.keyWindow?.contentViewController, let vc = ViewController.shared(), let note = getSelectedNotes()?.first else { return } let moveMenu = NSMenu() moveMenu.identifier = NSUserInterfaceItemIdentifier("fileMenu.history") let commits = note.getCommits() // Port if commits.count == 0 { return } for commit in commits { let menuItem = NSMenuItem() menuItem.title = commit.getDate() menuItem.representedObject = commit menuItem.action = #selector(vc.checkoutRevision(_:)) moveMenu.addItem(menuItem) } let general = moveMenu.item(at: 0) // Main window if cvc.isKind(of: ViewController.self), vc.notesTableView.selectedRow >= 0 { let view = vc.notesTableView.rect(ofRow: vc.notesTableView.selectedRow) let x = vc.splitView.subviews[0].frame.width + 5 moveMenu.popUp(positioning: general, at: NSPoint(x: x, y: view.origin.y + 8), in: vc.notesTableView) return } // Opened in new window if cvc.isKind(of: NoteViewController.self) { moveMenu.popUp(positioning: general, at: NSPoint(x: view.frame.width + 10, y: view.frame.height - 5), in: view) } } @IBAction func duplicate(_ sender: Any) { guard let notes = getSelectedNotes() else { return } for note in notes { let src = note.url let dst = NameHelper.generateCopy(file: note.url) if note.isTextBundle() || note.isEncrypted() { try? FileManager.default.copyItem(at: src, to: dst) continue } let name = dst.deletingPathExtension().lastPathComponent let noteDupe = Note(name: name, project: note.project, type: note.type, cont: note.container) noteDupe.content = NSMutableAttributedString(string: note.content.string) // Clone images if note.type == .Markdown && note.container == .none { let images = note.content.getImagesAndFiles() for image in images { noteDupe.move(from: image.url, imagePath: image.path, to: note.project, copy: true) } } if noteDupe.save() { Storage.shared().add(noteDupe) } ViewController.shared()?.notesTableView.insertRows(notes: [noteDupe]) } } @IBAction func importNote(_ sender: NSMenuItem) { guard let vc = ViewController.shared() else { return } let panel = NSOpenPanel() panel.allowsMultipleSelection = true panel.canChooseDirectories = false panel.canChooseFiles = true panel.canCreateDirectories = false panel.begin { (result) -> Void in if result == NSApplication.ModalResponse.OK { let urls = panel.urls if let project = vc.sidebarOutlineView.getSelectedProject() ?? Storage.shared().getDefault() { for url in urls { _ = vc.copy(project: project, url: url) } } } } } @objc func moveNote(_ sender: NSMenuItem) { let project = sender.representedObject as! Project guard let notes = getSelectedNotes() else { return } ViewController.shared()?.moveReq(notes: notes, project: project) { success in guard success else { return } if let cvc = NSApplication.shared.keyWindow?.contentViewController, cvc.isKind(of: NoteViewController.self) { self.updateTitle(note: notes.first!) } } } @IBAction func toggleContainer(_ sender: NSMenuItem) { guard let notes = getSelectedNotes() else { return } var newContainer: NoteContainer = .textBundleV2 if notes.first?.container == .textBundle || notes.first?.container == .textBundleV2 { newContainer = .none } for note in notes { if note.container == .encryptedTextPack { continue } note.convertContainer(to: newContainer) } } @IBAction func openWindow(_ sender: Any) { guard let currentNote = ViewController.shared()?.notesTableView.getSelectedNote() else { return } openInNewWindow(note: currentNote) } @IBAction func moveMenu(_ sender: Any) { guard let vc = ViewController.shared() else { return } // Move menu right from notes table view if let cvc = NSApplication.shared.keyWindow?.contentViewController, cvc.isKind(of: ViewController.self) { if vc.notesTableView.selectedRow >= 0 { vc.loadMoveMenu() let moveTitle = NSLocalizedString("Move", comment: "Menu") let moveMenu = vc.noteMenu.item(withTitle: moveTitle) let view = vc.notesTableView.rect(ofRow: vc.notesTableView.selectedRow) let x = vc.splitView.subviews[0].frame.width + 5 let general = moveMenu?.submenu?.item(at: 0) moveMenu?.submenu?.popUp(positioning: general, at: NSPoint(x: x, y: view.origin.y + 8), in: vc.notesTableView) } return // Move menu right from window } else { vc.loadMoveMenu() let moveTitle = NSLocalizedString("Move", comment: "Menu") let moveMenu = vc.noteMenu.item(withTitle: moveTitle) let general = moveMenu?.submenu?.item(at: 0) moveMenu?.submenu?.popUp(positioning: general, at: NSPoint(x: view.frame.width + 10, y: view.frame.height - 5), in: view) } } public func removeNotes(notes: [Note], forceRemove: Bool = false, rows: IndexSet? = nil) { guard let vc = ViewController.shared() else { return } let si = vc.getSidebarItem() if si?.isTrash() == true || forceRemove { vc.removeForever() // Call from window, close it! if let cvc = NSApplication.shared.keyWindow?.contentViewController, cvc.isKind(of: NoteViewController.self) { DispatchQueue.main.async { self.view.window?.close() } } return } let currentNote = vc.editor.note let shouldClearEditor = currentNote != nil && notes.contains(where: { $0 === currentNote }) UserDataService.instance.searchTrigger = true vc.notesTableView.removeRows(notes: notes) // Delete tags for note in notes { let tags = note.tags note.tags.removeAll() vc.sidebarOutlineView.removeTags(tags) } vc.storage.removeNotes(notes: notes) { urlMapping in if let md = AppDelegate.mainWindowController { let undoManager = md.notesListUndoManager if let ntv = vc.notesTableView { // Register undo (restore) undoManager.registerUndo(withTarget: ntv, selector: #selector(ntv.unDelete), object: urlMapping) undoManager.setActionName(NSLocalizedString("Delete", comment: "")) } if let rows = rows, let minRow = rows.min(), minRow > -1 { let qty = vc.notesTableView.countNotes() if qty > minRow { vc.notesTableView.selectRow(minRow) } else { vc.notesTableView.selectRow(qty - 1) } } UserDataService.instance.searchTrigger = false } if shouldClearEditor { vc.editor.clear() } } // Call from window, close it! if let cvc = NSApplication.shared.keyWindow?.contentViewController, cvc.isKind(of: NoteViewController.self) { DispatchQueue.main.async { self.view.window?.close() } return } // If is main window – focus to notes list if let cvc = NSApplication.shared.keyWindow?.contentViewController, cvc.isKind(of: ViewController.self) { NSApp.mainWindow?.makeFirstResponder(vc.notesTableView) } } @IBAction func actualSize(_ sender: Any) { UserDefaultsManagement.codeFont = NSFont(descriptor: UserDefaultsManagement.codeFont.fontDescriptor, size: CGFloat(UserDefaultsManagement.DefaultFontSize))! UserDefaultsManagement.noteFont = NSFont(descriptor: UserDefaultsManagement.noteFont.fontDescriptor, size: CGFloat(UserDefaultsManagement.DefaultFontSize))! ViewController.shared()?.reloadFonts() } @IBAction func zoomIn(_ sender: Any) { UserDefaultsManagement.codeFont = NSFont(descriptor: UserDefaultsManagement.codeFont.fontDescriptor, size: UserDefaultsManagement.codeFont.pointSize + 1)! UserDefaultsManagement.noteFont = NSFont(descriptor: UserDefaultsManagement.noteFont.fontDescriptor, size: UserDefaultsManagement.noteFont.pointSize + 1)! ViewController.shared()?.reloadFonts() } @IBAction func zoomOut(_ sender: Any) { UserDefaultsManagement.codeFont = NSFont(descriptor: UserDefaultsManagement.codeFont.fontDescriptor, size: UserDefaultsManagement.codeFont.pointSize - 1)! UserDefaultsManagement.noteFont = NSFont(descriptor: UserDefaultsManagement.noteFont.fontDescriptor, size: UserDefaultsManagement.noteFont.pointSize - 1)! ViewController.shared()?.reloadFonts() } @IBAction func showBackLinks(_ sender: NSMenuItem) { if let appDelegate = NSApplication.shared.delegate as? AppDelegate, let cvc = NSApplication.shared.keyWindow?.contentViewController as? EditorViewController, let note = cvc.vcEditor?.note { ViewController.shared()?.editor.clear() appDelegate.search(query: "[[" + note.title + "]]") } } // MARK: Dep methods public func openInNewWindow(note: Note, frame: NSRect? = nil, preview: Bool = false) { guard let windowController = NSStoryboard(name: "Main", bundle: nil) .instantiateController(withIdentifier: "noteWindowController") as? NSWindowController else { return } windowController.showWindow(nil) windowController.window?.makeKeyAndOrderFront(windowController) let viewController = windowController.contentViewController as! NoteViewController viewController.initWindow() viewController.editor.changePreviewState(preview) viewController.editor.fill(note: note) if note.isEncryptedAndLocked() { viewController.lockUnlockButton.image = NSImage(named: NSImage.lockLockedTemplateName) viewController.toggleNotesLock(self) } else { viewController.lockUnlockButton.image = NSImage(named: NSImage.lockUnlockedTemplateName) } AppDelegate.noteWindows.insert(windowController, at: 0) DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { if let frame = frame { windowController.window?.setFrame(frame, display: true) } viewController.view.window?.makeFirstResponder(viewController.editor) } } func cancelTextSearch() { let menu = NSMenuItem(title: "", action: nil, keyEquivalent: "") menu.tag = NSTextFinder.Action.hideFindInterface.rawValue vcEditor?.performTextFinderAction(menu) } func disablePreview() { guard let textView = self.vcEditor else { return } textView.disablePreviewEditorAndNote() textView.markdownView?.getScrollPosition { point in self.vcEditor?.note?.contentOffsetWeb = point } textView.markdownView?.removeFromSuperview() textView.markdownView = nil textView.subviews.removeAll(where: { $0.isKind(of: MPreviewView.self) }) refillEditArea() } public func viewDidResize() { guard vcEditor?.isPreviewEnabled() == true else { return } if noteLoading != .incomplete { previewResizeTimer.invalidate() previewResizeTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(reloadPreview), userInfo: nil, repeats: false) } } @objc private func reloadPreview() { DispatchQueue.main.async { MPreviewView.template = nil self.refillEditArea(force: true) } } public func updateTitle(note: Note) { guard let vcTitleLabel = vcTitleLabel else { return } var titleString = note.getFileName() if titleString.isValidUUID { titleString = String() } if titleString.count > 0 { vcTitleLabel.stringValue = note.project.getNestedLabel() + " › " + titleString } else { vcTitleLabel.stringValue = note.project.getNestedLabel() } vcTitleLabel.currentEditor()?.selectedRange = NSRange(location: 0, length: 0) view.window?.title = vcTitleLabel.stringValue } func refillEditArea(force: Bool = false) { noteLoading = .incomplete vcPreviewButton?.state = vcEditor?.isPreviewEnabled() == true ? .on : .off if let note = vcEditor?.note { vcEditor?.fill(note: note, force: force) } noteLoading = .done } public func unLock(notes: [Note]) { getMasterPassword() { password in guard password.count > 0 else { return } var i = 0 for note in notes { let success = note.unLock(password: password) if success { let insertTags = note.scanContentTags().0 DispatchQueue.main.async { ViewController.shared()?.sidebarOutlineView?.addTags(insertTags) ViewController.shared()?.notesTableView.reloadRow(note: note) } if i == 0 { note.password = password DispatchQueue.main.async { self.reloadAllOpenedWindows(note: note) } } } i = i + 1 } } } public func reloadAllOpenedWindows(note: Note) { let editors = AppDelegate.getEditTextViews() for editor in editors { if editor.note == note { editor.editorViewController?.refillEditArea(force: true) let lockIcon = note.isEncryptedAndLocked() ? NSImage.lockLockedTemplateName : NSImage.lockUnlockedTemplateName let lockImage = NSImage(named: lockIcon) if let noteVC = editor.editorViewController as? NoteViewController { noteVC.lockUnlockButton.image = lockImage } if let mainVC = editor.editorViewController as? ViewController { mainVC.lockUnlock.image = lockImage } editor.window?.makeFirstResponder(editor) } } } public func closeAllOpenedWindows(where note: Note) { for editor in AppDelegate.getOpenedEditTextViews() { if editor.note == note { editor.window?.close() } } } public func getMasterPassword(forEncrypt: Bool = false, completion: @escaping (String) -> ()) { if #available(OSX 10.12.2, *), UserDefaultsManagement.allowTouchID { let context = LAContext() context.localizedFallbackTitle = NSLocalizedString("Enter Master Password", comment: "") var passwordExist = false do { let item = KeychainPasswordItem(service: KeychainConfiguration.serviceName, account: "Master Password") let password = try item.readPassword() passwordExist = password.count > 0 } catch {/*_*/} guard passwordExist && context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) else { masterPasswordPrompt(validation: forEncrypt, completion: completion) return } context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "To access secure data") { (success, evaluateError) in // Skip if cancelled if let error = evaluateError as NSError? { if error.code == LAError.userCancel.rawValue || error.code == LAError.appCancel.rawValue { return } } // Press enter password or failed TouchID if !success { self.masterPasswordPrompt(validation: forEncrypt, completion: completion) return } do { let item = KeychainPasswordItem(service: KeychainConfiguration.serviceName, account: "Master Password") let password = try item.readPassword() completion(password) return } catch { print("Keychain error: \(error.localizedDescription)") } // No password in keychain self.masterPasswordPrompt(validation: forEncrypt, completion: completion) } } else { // Bio is not available or disabled masterPasswordPrompt(validation: forEncrypt, completion: completion) } } @IBAction func onOkClick(_ sender: Any?) { guard let passwordField = encPassword, let verifyPasswordField = encVerifyPassword, let window = self.view.window else { return } if passwordField.stringValue.count == 0 { let alert = NSAlert() alert.alertStyle = .critical alert.informativeText = NSLocalizedString("Please try again", comment: "") alert.messageText = NSLocalizedString("Empty password", comment: "") alert.beginSheetModal(for: window) { (returnCode: NSApplication.ModalResponse) -> Void in } return } if passwordField.stringValue != verifyPasswordField.stringValue { let alert = NSAlert() alert.alertStyle = .critical alert.informativeText = NSLocalizedString("Please try again", comment: "") alert.messageText = NSLocalizedString("Wrong repeated password", comment: "") alert.beginSheetModal(for: window) { (returnCode: NSApplication.ModalResponse) -> Void in } return } if let encCompletionHandler = encCompletionHandler { encCompletionHandler(passwordField.stringValue) } self.alert?.window.close() } private func masterPasswordPrompt(validation: Bool = false, completion: @escaping (String) -> ()) { DispatchQueue.main.async { guard var window = self.view.window else { return } if NSApplication.shared.keyWindow?.contentViewController?.isKind(of: NoteViewController.self) == true, let evc = NSApplication.shared.keyWindow?.contentViewController as? EditorViewController, let currentWin = evc.view.window { window = currentWin } self.alert = NSAlert() guard let alert = self.alert else { return } alert.alertStyle = .informational if validation { alert.messageText = NSLocalizedString("Enter an encryption password:", comment: "") alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) alert.buttons[0].target = self alert.buttons[0].action = #selector(self.onOkClick(_:)) // Create the NSTextFields and labels let newPasswordLabel = NSTextField(labelWithString: NSLocalizedString("Password:", comment: "")) let newPasswordField = NSSecureTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24)) let repeatPasswordLabel = NSTextField(labelWithString: NSLocalizedString("Verify Password:", comment: "")) let repeatPasswordField = NSSecureTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24)) self.encPassword = newPasswordField self.encVerifyPassword = repeatPasswordField self.encCompletionHandler = completion newPasswordLabel.alignment = .right repeatPasswordLabel.alignment = .right // Add the labels and text fields to a custom view let containerView = NSView(frame: NSRect(x: 0, y: 0, width: 400, height: 60)) containerView.translatesAutoresizingMaskIntoConstraints = false newPasswordLabel.translatesAutoresizingMaskIntoConstraints = false newPasswordField.translatesAutoresizingMaskIntoConstraints = false repeatPasswordLabel.translatesAutoresizingMaskIntoConstraints = false repeatPasswordField.translatesAutoresizingMaskIntoConstraints = false containerView.addSubview(newPasswordLabel) containerView.addSubview(newPasswordField) containerView.addSubview(repeatPasswordLabel) containerView.addSubview(repeatPasswordField) // Set the custom view as the accessory view for the NSAlert alert.accessoryView = containerView // Define constraints NSLayoutConstraint.activate([ newPasswordLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 8), newPasswordLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 8), newPasswordField.leadingAnchor.constraint(equalTo: newPasswordLabel.trailingAnchor, constant: 8), newPasswordField.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 0), newPasswordField.widthAnchor.constraint(equalToConstant: 200), newPasswordField.centerYAnchor.constraint(equalTo: newPasswordLabel.centerYAnchor), repeatPasswordLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 8), repeatPasswordLabel.topAnchor.constraint(equalTo: newPasswordLabel.bottomAnchor, constant: 8), repeatPasswordField.leadingAnchor.constraint(equalTo: repeatPasswordLabel.trailingAnchor, constant: 8), repeatPasswordField.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 0), repeatPasswordField.widthAnchor.constraint(equalToConstant: 200), repeatPasswordField.centerYAnchor.constraint(equalTo: repeatPasswordLabel.centerYAnchor), containerView.widthAnchor.constraint(equalToConstant: 400), containerView.heightAnchor.constraint(equalToConstant: 60), ]) // Show the NSAlert alert.beginSheetModal(for: window) { (returnCode: NSApplication.ModalResponse) -> Void in self.alert = nil } newPasswordField.becomeFirstResponder() return } let field = NSSecureTextField(frame: NSRect(x: 0, y: 0, width: 290, height: 20)) alert.accessoryView = field alert.messageText = NSLocalizedString("Master password:", comment: "") alert.informativeText = NSLocalizedString("Please enter password for current note", comment: "") alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) alert.beginSheetModal(for: window) { (returnCode: NSApplication.ModalResponse) -> Void in if returnCode == NSApplication.ModalResponse.alertFirstButtonReturn { completion(field.stringValue) } self.alert = nil } field.becomeFirstResponder() } } public func lockUnlocked(notes: [Note]) -> [Note] { var notes = notes var isFirst = true for note in notes { if note.isUnlocked() && note.isEncrypted() { if note.lock() && isFirst { reloadAllOpenedWindows(note: note) } removeTags(note: note) notes.removeAll { $0 === note } } isFirst = false ViewController.shared()?.notesTableView.reloadRow(note: note) } // Focus notes list if active main window if let vc = view.window?.contentViewController as? ViewController, let mainWindow = view.window { mainWindow.makeFirstResponder(vc.notesTableView) } return notes } public func decryptUnlocked(notes: [Note]) -> [Note] { var notes = notes for note in notes { if note.isUnlocked() { if note.unEncryptUnlocked() { notes.removeAll { $0 === note } ViewController.shared()?.notesTableView.reloadRow(note: note) } } } return notes } public func removeTags(note: Note) { let tags = note.tags note.tags = [] ViewController.shared()?.sidebarOutlineView?.removeTags(tags) } public func dropTitle() { let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ?? "FSNotes" vcTitleLabel?.stringValue = appName view.window?.title = appName } func focusEditArea() { guard let editor = vcEditor, let note = editor.note, !editor.isPreviewEnabled(), note.container != .encryptedTextPack else { return } editor.window?.makeFirstResponder(editor) if let ntv = ViewController.shared()?.notesTableView, ntv.selectedRow > -1 { vcEditor?.isEditable = true vcNonSelectedLabel?.isHidden = true } } // Changed main edit view func textDidChange(_ notification: Notification) { guard let editor = vcEditor, let note = editor.note, let vc = ViewController.shared() else { return } vc.prevCommit = nil if editor.isEditable { note.isBlocked = true editor.textStorage?.removeHighlight() note.save(attributed: editor.attributedString()) updateLastEditedStatus() vc.reSort(note: note) } breakUndoTimer.invalidate() breakUndoTimer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(breakUndo), userInfo: nil, repeats: true) } private func updateLastEditedStatus() { let editors = AppDelegate.getEditTextViews() for editor in editors { editor.isLastEdited = false } vcEditor?.isLastEdited = true } @objc func breakUndo() { guard let editor = vcEditor else { return } if ( editor.isPreviewEnabled() == false && editor.isEditable ) { editor.breakUndoCoalescing() } } public func createNote(name: String = "", content: String = "", folderName: String? = nil, openInNewWindow: Bool = false) -> Note? { guard let vc = ViewController.shared() else { return nil } var text = String() var project: Project? if let folderName = folderName { project = vc.sidebarOutlineView.getOrCreateProject(name: folderName) if let existProject = project, existProject.isEncrypted { project = nil } } let selectedProjects = vc.sidebarOutlineView.getSidebarProjects() var sidebarProject = project ?? selectedProjects?.first if sidebarProject == nil { sidebarProject = Storage.shared().getDefault() } guard let project = sidebarProject, !project.isLocked() else { return nil } if !name.isEmpty, [.autoRename, .autoRenameNew].contains(UserDefaultsManagement.naming) && UserDefaultsManagement.autoInsertHeader { text.append("# " + name + "\n\n") } if !content.isEmpty { text.append(content) } let inlineTags = vc.sidebarOutlineView.getSelectedInlineTags() if !inlineTags.isEmpty { text.append(inlineTags) } if let type = vc.getSidebarType(), type == .Todo, content.count == 0 { text = "- [ ] " } let note = Note(name: name, project: project) note.content = NSMutableAttributedString(string: text) if note.save() { Storage.shared().add(note) } _ = note.scanContentTags() if folderName == nil, let selectedProjects = selectedProjects, !selectedProjects.contains(project) { return note } if !openInNewWindow { disablePreview() vc.notesTableView.deselectNotes() vc.storage.searchQuery.dropFilter() vc.editor.string = text vc.editor.note = note vc.search.stringValue.removeAll() } vc.updateTable() { if openInNewWindow { return } DispatchQueue.main.async { vc.notesTableView.saveNavigationHistory(note: note) if let index = vc.notesTableView.getIndex(for: note) { vc.notesTableView.selectRowIndexes([index], byExtendingSelection: false) vc.notesTableView.scrollRowToVisible(index) } vc.focusEditArea() NSApp.activate(ignoringOtherApps: true) self.view.window?.makeKeyAndOrderFront(self) } } // Project encrypted and unlocked – encrypt by default if let password = project.password { if note.encrypt(password: password) { if note.unLock(password: password) { note.password = password } } } return note } } ================================================ FILE: FSNotes/Extensions/NSAppearance+.swift ================================================ // // NSAppearance+.swift // FSNotes // // Created by Oleksandr Glushchenko on 9/29/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import AppKit.NSAppearance extension NSAppearance { var isDark: Bool { if UserDefaultsManagement.appearanceType == .System { let mode = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") return mode == "Dark" } if self.name == .vibrantDark { return true } guard #available(macOS 10.14, *) else { return false } switch self.name { case .accessibilityHighContrastDarkAqua, .darkAqua, .accessibilityHighContrastVibrantDark: return true default: return false } } } ================================================ FILE: FSNotes/Extensions/NSColor+.swift ================================================ // // NSColor+.swift // FSNotes // // Created by Олександр Глущенко on 11.08.2021. // Copyright © 2021 Oleksandr Glushchenko. All rights reserved. // import Cocoa public extension NSColor { static var tagColor: NSColor { get { let accentColor = UserDefaults.standard.value(forKey: "AppleAccentColor") if #available(macOS 10.14, *), accentColor != nil { return NSColor.controlAccentColor } if #available(macOS 10.13, *) { return NSColor(named: "background_tag")! } return NSColor.gray } } var hexString: String { guard let rgbColor = usingColorSpace(.deviceRGB) else { return "#FFFFFF" } let red = Int(round(rgbColor.redComponent * 0xFF)) let green = Int(round(rgbColor.greenComponent * 0xFF)) let blue = Int(round(rgbColor.blueComponent * 0xFF)) let hexString = NSString(format: "#%02X%02X%02X", red, green, blue) return hexString as String } } ================================================ FILE: FSNotes/Extensions/NSFont+.swift ================================================ // // NSFont+.swift // FSNotes // // Created by Oleksandr Glushchenko on 8/26/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Cocoa extension NSFont { public var lineHeight: CGFloat { return CGFloat(ceilf(Float(ascender + abs(descender) + leading))) } public var lineHeightCustom: CGFloat { return CGFloat(ceilf(Float(ascender + abs(descender) + leading))) } public func getAttachmentHeight() -> Double { return Double(pointSize) + 6 } var isBold: Bool { return fontDescriptor.symbolicTraits.contains(.bold) } var isItalic: Bool { return fontDescriptor.symbolicTraits.contains(.italic) } var height:CGFloat { let constraintRect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) let boundingBox = "A".boundingRect(with: constraintRect, options: NSString.DrawingOptions.usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: self], context: nil) return boundingBox.height } static func italicFont() -> NSFont { return NSFontManager().convert(UserDefaultsManagement.noteFont, toHaveTrait: .italicFontMask) } static func boldFont() -> NSFont { return NSFontManager().convert(UserDefaultsManagement.noteFont, toHaveTrait: .boldFontMask) } func bold() -> NSFont { guard let family = UserDefaultsManagement.noteFont.familyName else { return UserDefaultsManagement.noteFont } var mask = 0 if (isItalic) { mask = NSFontBoldTrait|NSFontItalicTrait } else { mask = NSFontBoldTrait } if let font = NSFontManager().font(withFamily: family, traits: NSFontTraitMask(rawValue: NSFontTraitMask.RawValue(mask)), weight: 5, size: CGFloat(UserDefaultsManagement.fontSize)) { return font } return UserDefaultsManagement.noteFont } } ================================================ FILE: FSNotes/Extensions/NSImage+.swift ================================================ // // NSImage+.swift // FSNotesCore macOS // // Created by Oleksandr Glushchenko on 10/14/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa public extension NSImage { var height: CGFloat { return self.size.height } /// Returns the width of the current image. var width: CGFloat { return self.size.width } /// Returns a png representation of the current image. var PNGRepresentation: Data? { if let tiff = self.tiffRepresentation, let tiffData = NSBitmapImageRep(data: tiff) { return tiffData.representation(using: .png, properties: [:]) } return nil } /// Copies the current image and resizes it to the given size. /// /// - parameter size: The size of the new image. /// /// - returns: The resized copy of the given image. func copy(size: NSSize) -> NSImage? { // Create a new rect with given width and height let frame = NSRect(x: 0, y: 0, width: size.width, height: size.height) // Get the best representation for the given size. guard let rep = self.bestRepresentation(for: frame, context: nil, hints: nil) else { return nil } // Create an empty image with the given size. let img = NSImage(size: size) // Set the drawing context and make sure to remove the focus before returning. img.lockFocus() defer { img.unlockFocus() } // Draw the new image if rep.draw(in: frame) { return img } // Return nil in case something went wrong. return nil } /// Copies the current image and resizes it to the size of the given NSSize, while /// maintaining the aspect ratio of the original image. /// /// - parameter size: The size of the new image. /// /// - returns: The resized copy of the given image. func resizeWhileMaintainingAspectRatioToSize(size: NSSize) -> NSImage? { let newSize: NSSize let widthRatio = size.width / self.width let heightRatio = size.height / self.height if widthRatio > heightRatio { newSize = NSSize(width: floor(self.width * widthRatio), height: floor(self.height * widthRatio)) } else { newSize = NSSize(width: floor(self.width * heightRatio), height: floor(self.height * heightRatio)) } return self.copy(size: newSize) } /// Copies and crops an image to the supplied size. /// /// - parameter size: The size of the new image. /// /// - returns: The cropped copy of the given image. func crop(size: NSSize) -> NSImage? { // Resize the current image, while preserving the aspect ratio. guard let resized = self.resizeWhileMaintainingAspectRatioToSize(size: size) else { return nil } // Get some points to center the cropping area. let xCoord = floor((resized.width - size.width) / 2) let yCoord = floor((resized.height - size.height) / 2) // Create the cropping frame. let frame = NSRect(x: xCoord, y: yCoord, width: size.width, height: size.height) // Get the best representation of the image for the given cropping frame. guard let rep = resized.bestRepresentation(for: frame, context: nil, hints: nil) else { return nil } // Create a new image with the new size let img = NSImage(size: size) img.lockFocus() defer { img.unlockFocus() } if rep.draw( in: NSRect(x: 0, y: 0, width: size.width, height: size.height), from: frame, operation: NSCompositingOperation.copy, fraction: 1.0, respectFlipped: false, hints: [:]) { // Return the cropped image. return img } // Return nil in case anything fails. return nil } /// Saves the PNG representation of the current image to the HD. /// /// - parameter url: The location url to which to write the png file. func savePNGRepresentationToURL(url: URL) throws { if let png = self.PNGRepresentation { try png.write(to: url, options: .atomicWrite) } } func resize(to targetSize: CGSize) -> NSImage? { let frame = CGRect(x: 0, y: 0, width: targetSize.width, height: targetSize.height) guard let representation = bestRepresentation(for: frame, context: nil, hints: nil) else { return nil } let image = NSImage(size: targetSize, flipped: false, drawingHandler: { (_) -> Bool in return representation.draw(in: frame) }) return image } func resized(to newSize: NSSize) -> NSImage? { let scale = NSScreen.main?.backingScaleFactor ?? 1.0 let pixelWidth = Int(newSize.width * scale) let pixelHeight = Int(newSize.height * scale) if let bitmapRep = NSBitmapImageRep( bitmapDataPlanes: nil, pixelsWide: pixelWidth, pixelsHigh: pixelHeight, bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0 ) { bitmapRep.size = newSize NSGraphicsContext.saveGraphicsState() NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: bitmapRep) let rect = NSRect(x: 0, y: 0, width: newSize.width, height: newSize.height) draw(in: rect, from: .zero, operation: .copy, fraction: 1.0) NSGraphicsContext.restoreGraphicsState() let resizedImage = NSImage(size: newSize) resizedImage.addRepresentation(bitmapRep) return resizedImage } return nil } /// Copy the image and resize it to the supplied size, while maintaining it's /// original aspect ratio. /// /// - Parameter size: The target size of the image. /// - Returns: The resized image. func resizeMaintainingAspectRatio(to targetSize: CGSize) -> NSImage? { let widthRatio = targetSize.width / size.width let heightRatio = targetSize.height / size.height let ratio = max(widthRatio, heightRatio) let newSize = CGSize(width: floor(size.width * ratio), height: floor(size.height * ratio)) return resized(to: NSSize(width: newSize.width, height: newSize.height)) } // MARK: Cropping /// Resize the image, to nearly fit the supplied cropping size /// and return a cropped copy the image. /// /// - Parameter size: The size of the new image. /// - Returns: The cropped image. func crop(to targetSize: CGSize) -> NSImage? { // Resize the current image, while preserving the aspect ratio. guard let resized = resizeMaintainingAspectRatio(to: targetSize) else { return nil } // Get some points to center the cropping area. let yCoord = floor(resized.size.height - targetSize.height) // Create the cropping frame. let frame = CGRect(origin: CGPoint(x: 0, y: yCoord), size: targetSize) // Get the best representation of the image for the given cropping frame. guard let representation = resized.bestRepresentation(for: frame, context: nil, hints: nil) else { return nil } // Create a new image with the new size let cropped = NSImage(size: targetSize) cropped.lockFocus() defer { cropped.unlockFocus() } let outputFrame = CGRect(origin: CGPoint(x: 0, y: 0), size: targetSize) guard representation.draw(in: outputFrame, from: frame, operation: .copy, fraction: 1.0, respectFlipped: false, hints: [:]) else { return nil } return cropped } var jpgData: Data? { guard let tiffRepresentation = tiffRepresentation, let bitmapImage = NSBitmapImageRep(data: tiffRepresentation) else { return nil } return bitmapImage.representation(using: .jpeg, properties: [:]) } func tint(color: NSColor) -> NSImage { if let image = self.copy() as? NSImage { image.lockFocus() color.set() let imageRect = NSRect(origin: .zero, size: image.size) imageRect.fill(using: .sourceAtop) image.unlockFocus() return image } return self } func roundCorners(withRadius radius: CGFloat) -> NSImage { let rect = NSRect(origin: NSPoint.zero, size: size) if let cgImage = self.cgImage, let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: 4 * Int(size.width), space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue) { context.beginPath() context.addPath(CGPath(roundedRect: rect, cornerWidth: radius, cornerHeight: radius, transform: nil)) context.closePath() context.clip() context.draw(cgImage, in: rect) if let composedImage = context.makeImage() { return NSImage(cgImage: composedImage, size: size) } } return self } var cgImage: CGImage? { var rect = CGRect.init(origin: .zero, size: self.size) return self.cgImage(forProposedRect: &rect, context: nil, hints: nil) } } ================================================ FILE: FSNotes/Extensions/NSWindow+.swift ================================================ // // NSWindow+.swift // FSNotes // // Created by Oleksandr Hlushchenko on 10.07.2022. // Copyright © 2022 Oleksandr Hlushchenko. All rights reserved. // import Cocoa extension NSWindow { public func setFrameOriginToPositionWindowInCenterOfScreen() { if let screenSize = screen?.frame.size { let origin = NSPoint(x: (screenSize.width-800)/2, y: (screenSize.height-600)/2) self.setFrame(NSRect(origin: origin, size: CGSize(width: 800, height: 600)), display: true) } } } ================================================ FILE: FSNotes/Extensions/UserDefaultsManagement+.swift ================================================ // // UserDefaultsManagement+.swift // FSNotesCore macOS // // Created by Oleksandr Glushchenko on 10/25/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation import MASShortcut import AppKit extension UserDefaultsManagement { private struct Constants { static let ActivateKeyCode = "activateKeyCode" static let ActivateKeyModifier = "activateKeyModifier" static let AppearanceTypeKey = "appearanceType2025" static let codeTheme = "codeTheme" static let codeThemeDark = "codeThemeDark" static let darkMode = "darkMode" static let dockIcon = "dockIcon" static let NewNoteKeyModifier = "newNoteKeyModifier" static let NewNoteKeyCode = "newNoteKeyCode" static let SearchNoteKeyCode = "searchNoteKeyCode" static let SearchNoteKeyModifier = "searchNoteKeyModifier" static let ProjectsKey = "projects" static let QuickNoteKey = "quickNoteKey" static let QuickNoteKeyModifier = "quickNoteKeyModifier" } static var appearanceType: AppearanceType { get { if let result = UserDefaults.standard.object(forKey: Constants.AppearanceTypeKey) as? Int { return AppearanceType(rawValue: result)! } return AppearanceType.System } set { UserDefaults.standard.set(newValue.rawValue, forKey: Constants.AppearanceTypeKey) } } static var newNoteShortcut: MASShortcut? { get { let code = UserDefaults.standard.object(forKey: Constants.NewNoteKeyCode) let modifier = UserDefaults.standard.object(forKey: Constants.NewNoteKeyModifier) if code != nil && modifier != nil, let keyCode = code as? UInt, let modifierFlags = modifier as? UInt { if (code as? Int) == 0 && (modifier as? Int) == 0 { return nil } return MASShortcut(keyCode: keyCode, modifierFlags: modifierFlags) } return MASShortcut(keyCode: 45, modifierFlags: 917504) } set { let code = newValue?.keyCode ?? 0 let modifier = newValue?.modifierFlags ?? 0 UserDefaults.standard.set(code, forKey: Constants.NewNoteKeyCode) UserDefaults.standard.set(modifier, forKey: Constants.NewNoteKeyModifier) } } static var quickNoteShortcut: MASShortcut? { get { let code = UserDefaults.standard.object(forKey: Constants.QuickNoteKey) let modifier = UserDefaults.standard.object(forKey: Constants.QuickNoteKeyModifier) if code != nil && modifier != nil, let keyCode = code as? UInt, let modifierFlags = modifier as? UInt { if (code as? Int) == 0 && (modifier as? Int) == 0 { return nil } return MASShortcut(keyCode: keyCode, modifierFlags: modifierFlags) } return MASShortcut(keyCode: 31, modifierFlags: 917504) } set { let code = newValue?.keyCode ?? 0 let modifier = newValue?.modifierFlags ?? 0 UserDefaults.standard.set(code, forKey: Constants.QuickNoteKey) UserDefaults.standard.set(modifier, forKey: Constants.QuickNoteKeyModifier) } } static var searchNoteShortcut: MASShortcut? { get { let code = UserDefaults.standard.object(forKey: Constants.SearchNoteKeyCode) let modifier = UserDefaults.standard.object(forKey: Constants.SearchNoteKeyModifier) if code != nil && modifier != nil, let keyCode = code as? UInt, let modifierFlags = modifier as? UInt { if (code as? Int) == 0 && (modifier as? Int) == 0 { return nil } return MASShortcut(keyCode: keyCode, modifierFlags: modifierFlags) } return MASShortcut(keyCode: 37, modifierFlags: 917504) } set { let code = newValue?.keyCode ?? 0 let modifier = newValue?.modifierFlags ?? 0 UserDefaults.standard.set(code, forKey: Constants.SearchNoteKeyCode) UserDefaults.standard.set(modifier, forKey: Constants.SearchNoteKeyModifier) } } static var activateShortcut: MASShortcut? { get { let code = UserDefaults.standard.object(forKey: Constants.ActivateKeyCode) let modifier = UserDefaults.standard.object(forKey: Constants.ActivateKeyModifier) if code != nil && modifier != nil, let keyCode = code as? UInt, let modifierFlags = modifier as? UInt { if (code as? Int) == 0 && (modifier as? Int) == 0 { return nil } return MASShortcut(keyCode: keyCode, modifierFlags: modifierFlags) } return MASShortcut(keyCode: 40, modifierFlags: 917504) } set { let code = newValue?.keyCode ?? 0 let modifier = newValue?.modifierFlags ?? 0 UserDefaults.standard.set(code, forKey: Constants.ActivateKeyCode) UserDefaults.standard.set(modifier, forKey: Constants.ActivateKeyModifier) } } static var dockIcon: Int { get { if let tag = UserDefaults.standard.object(forKey: Constants.dockIcon) as? Int { return tag } return 0 } set { UserDefaults.standard.set(newValue, forKey: Constants.dockIcon) } } static var noteFont: NSFont { get { if let name = fontName, name.starts(with: ".") { return NSFont.systemFont(ofSize: CGFloat(self.fontSize)) } if let fontName = self.fontName, let font = NSFont(name: fontName, size: CGFloat(self.fontSize)) { return font } return NSFont.systemFont(ofSize: CGFloat(self.fontSize)) } set { self.fontName = newValue.fontName self.fontSize = Int(newValue.pointSize) } } static var codeFont: NSFont { get { if let font = NSFont(name: self.codeFontName, size: CGFloat(self.codeFontSize)) { return font } return NSFont.systemFont(ofSize: CGFloat(self.codeFontSize)) } set { self.codeFontName = newValue.familyName ?? "Source Code Pro" self.codeFontSize = Int(newValue.pointSize) } } } ================================================ FILE: FSNotes/FSNotes (CloudKit).entitlements ================================================ com.apple.developer.aps-environment development com.apple.developer.icloud-container-environment Production com.apple.developer.icloud-container-identifiers iCloud.co.fluder.fsnotes com.apple.developer.icloud-services CloudDocuments com.apple.developer.ubiquity-container-identifiers iCloud.co.fluder.fsnotes com.apple.developer.ubiquity-kvstore-identifier $(TeamIdentifierPrefix)co.fluder.fsnotes com.apple.security.app-sandbox com.apple.security.files.user-selected.read-write com.apple.security.network.client com.apple.security.print ================================================ FILE: FSNotes/FSNotes.entitlements ================================================ com.apple.security.app-sandbox com.apple.security.files.user-selected.read-write com.apple.security.network.client com.apple.security.print ================================================ FILE: FSNotes/Helpers/FSNTextAttachmentCell.swift ================================================ // // FSNTextAttahcmentCell.swift // FSNotes // // Created by Олександр Глущенко on 25.11.2020. // Copyright © 2020 Oleksandr Glushchenko. All rights reserved. // import Cocoa class FSNTextAttachmentCell: NSTextAttachmentCell { let textContainer: NSTextContainer init(textContainer: NSTextContainer, image: NSImage) { self.textContainer = textContainer super.init(imageCell: image) } required init(coder: NSCoder) { self.textContainer = NSTextContainer() super.init(coder: coder) } override func cellSize() -> NSSize { let size = super.cellSize() if size.height == UserDefaultsManagement.noteFont.getAttachmentHeight() { return size } return NSSize(width: textContainer.size.width, height: size.height) } override nonisolated func cellBaselineOffset() -> NSPoint { return NSPoint(x: 0, y: -2) } } ================================================ FILE: FSNotes/Helpers/FileSystemEventManager.swift ================================================ // // FileSystemEventManager.swift // FSNotes // // Created by Oleksandr Glushchenko on 7/13/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation class FileSystemEventManager { private var storage: Storage private var delegate: ViewController private var watcher: FileWatcher? private var observedFolders: [String] private var textBundleItems = ["text.markdown", "text.md", "text.txt", "info.json"] init(storage: Storage, delegate: ViewController) { self.storage = storage self.delegate = delegate self.observedFolders = self.storage.getProjectPaths() } public func start() { watcher = FileWatcher(self.observedFolders) watcher?.callback = { event in guard let path = event.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else { return } guard let url = URL(string: "file://" + path) else { return } if !event.path.contains(".textbundle") && ( event.dirRemoved || event.dirCreated || event.dirRenamed || event.dirChange ) { self.handleDirEvents(event: event) return } if url.lastPathComponent == ".encrypt" { self.loadEncryptionStatus(url: url) return } if !self.storage.isValidNote(url: url) { return } if event.fileRemoved || event.dirRemoved { guard let note = self.storage.getBy(url: url) else { return } self.removeNote(note: note) } let fullUrl = self.handleTextBundle(url: url) // Resolve conflicts if exist if UserDefaultsManagement.automaticConflictsResolution, let note = self.storage.getBy(url: fullUrl) { self.resolveConflict(url: note.url) } if event.fileRenamed || event.dirRenamed { self.moveHandler(url: fullUrl, pathList: self.observedFolders) return } guard self.checkFile(url: fullUrl, pathList: self.observedFolders) else { return } // Order is important, invoke only before change if event.fileCreated { self.importNote(fullUrl) return } if event.fileChange || event.dirChange, let note = self.storage.getBy(url: fullUrl) { self.reloadNote(note: note) } } watcher?.start() } private func handleDirEvents(event: FileWatcherEvent) { guard !event.path.contains("Trash") else { return } let dirURL = URL(fileURLWithPath: event.path, isDirectory: true) let project = self.storage.getProjectBy(url: dirURL) if dirURL.path.contains("/.") { return } guard !dirURL.isHidden() else { // hide if exist and hidden (xattr "es.fsnot.hidden.dir") if event.dirChange { if let project = project { OperationQueue.main.addOperation { self.delegate.sidebarOutlineView.removeRows(projects: [project]) } } } return } if event.dirRenamed { if let project = project { // hack: occasionally get rename event when created if !FileManager.default.fileExists(atPath: dirURL.path) { OperationQueue.main.addOperation { self.delegate.sidebarOutlineView.removeRows(projects: [project]) } } } else { if FileManager.default.directoryExists(atUrl: dirURL) { OperationQueue.main.addOperation { if let projects = self.storage.insert(url: dirURL) { self.delegate.sidebarOutlineView.insertRows(projects: projects) } } } } return } if event.dirRemoved { if let project = project { OperationQueue.main.addOperation { self.delegate.sidebarOutlineView.removeRows(projects: [project]) } } return } // dirChange on xattr "es.fsnot.hidden.dir" changed if event.dirCreated || ( event.dirChange && dirURL.hasNonHiddenBit() ) { OperationQueue.main.addOperation { if let projects = self.storage.insert(url: dirURL) { self.delegate.sidebarOutlineView.insertRows(projects: projects) } } return } } private func moveHandler(url: URL, pathList: [String]) { let fileExistInFS = self.checkFile(url: url, pathList: pathList) guard let note = self.storage.getBy(url: url) else { if fileExistInFS { self.importNote(url) } return } if fileExistInFS { renameNote(note: note) return } removeNote(note: note) } private func checkFile(url: URL, pathList: [String]) -> Bool { return ( FileManager.default.fileExists(atPath: url.path) && self.storage.isValidNote(url: url) && pathList.contains(url.deletingLastPathComponent().path) ) } private func importNote(_ url: URL) { let url = self.handleTextBundle(url: url) let n = storage.getBy(url: url) guard n == nil else { if let nUnwrapped = n, nUnwrapped.url == UserDataService.instance.focusOnImport { self.delegate.updateTable() { self.delegate.notesTableView.setSelected(note: nUnwrapped) UserDataService.instance.focusOnImport = nil } // When git checkout .textbundle/text.md system trigger remove/create events // but the note is not deleted, so the note must be reloaded } else if let nUnwrapped = n { reloadNote(note: nUnwrapped) } return } guard let note = storage.importNote(url: url) else { return } DispatchQueue.main.async { if let url = UserDataService.instance.focusOnImport, let note = self.storage.getBy(url: url) { self.delegate.updateTable() { self.delegate.notesTableView.setSelected(note: note) UserDataService.instance.focusOnImport = nil } } else { if !note.isTrash() { OperationQueue.main.addOperation { self.delegate.notesTableView.insertRows(notes: [note]) } } } } } private func renameNote(note: Note) { if note.url == UserDataService.instance.focusOnImport { self.delegate.updateTable() { self.delegate.notesTableView.setSelected(note: note) UserDataService.instance.focusOnImport = nil } // On TextBundle import } else { self.reloadNote(note: note) } } private func removeNote(note: Note) { print("FSWatcher remove note: \"\(note.name)\"") self.storage.removeNotes(notes: [note], fsRemove: false) { _ in DispatchQueue.main.async { if self.delegate.notesTableView.numberOfRows > 0 { self.delegate.notesTableView.removeRows(notes: [note]) } } } } private func reloadNote(note: Note) { guard !note.isBlocked, note.container != .encryptedTextPack else { return } guard let modificationDate = note.getFileModifiedDate(), let creationDate = note.getFileCreationDate() else { return } if modificationDate.isGreaterThan(note.modifiedLocalAt) { note.modifiedLocalAt = modificationDate note.cacheHash = nil guard var fsContent = note.getContent() else { return } _ = fsContent.loadAttachments(note) // Trying load content from encrypted note with current password if note.url.pathExtension == "etp", let password = note.password, note.unLock(password: password) { fsContent = note.content } note.content = fsContent // tags changes let result = note.scanContentTags() if result.0.count > 0 { DispatchQueue.main.async { self.delegate.sidebarOutlineView.insertTags(note: note) } } if result.1.count > 0 { DispatchQueue.main.async { self.delegate.sidebarOutlineView.removeTags(result.1) } } // reload view self.delegate.notesTableView.reloadRow(note: note) self.delegate.reSort(note: note) let editors = AppDelegate.getEditTextViews() for editor in editors { if editor.note == note { DispatchQueue.main.async { editor.editorViewController?.refillEditArea(force: true) } } } } if creationDate != note.creationDate { note.creationDate = creationDate delegate.notesTableView.reloadDate(note: note) delegate.reSort(note: note) // Reload images if note moved (cache invalidated) note.loadPreviewInfo() } } private func handleTextBundle(url: URL) -> URL { if self.textBundleItems.contains(url.lastPathComponent) && url.path.contains(".textbundle") { let path = url.deletingLastPathComponent().path return URL(fileURLWithPath: path, isDirectory: false) } return url } public func restart() { watcher?.stop() self.observedFolders = self.storage.getProjectPaths() start() } public func reloadObservedFolders() { self.observedFolders = self.storage.getProjectPaths() } public func resolveConflict(url: URL) { if let conflicts = NSFileVersion.unresolvedConflictVersionsOfItem(at: url as URL) { for conflict in conflicts { guard let modificationDate = conflict.modificationDate else { continue } guard let localizedName = conflict.localizedName else { continue } let localizedUrl = URL(fileURLWithPath: localizedName) let ext = url.pathExtension let name = localizedUrl.deletingPathExtension().lastPathComponent let dateFormatter = ISO8601DateFormatter() dateFormatter.formatOptions = [ .withYear, .withMonth, .withDay, .withTime ] let dateString: String = dateFormatter.string(from: modificationDate) let conflictName = "\(name) (CONFLICT \(dateString)).\(ext)" let to = url.deletingLastPathComponent().appendingPathComponent(conflictName) if FileManager.default.fileExists(atPath: to.path) { conflict.isResolved = true continue } // Reload current encrypted note let editors = AppDelegate.getEditTextViews() for editor in editors { if let currentNote = editor.note, currentNote.url == url { if let password = currentNote.password, ext == "etp" { _ = currentNote.unLock(password: password) } DispatchQueue.main.async { editor.editorViewController?.refillEditArea(force: true) } } } do { try FileManager.default.copyItem(at: conflict.url, to: to) var attributes = [FileAttributeKey : Any]() attributes[.posixPermissions] = 0o777 try FileManager.default.setAttributes(attributes, ofItemAtPath: to.path) } catch let error { print("Conflict resolving error: ", error) } conflict.isResolved = true } } } private func loadEncryptionStatus(url: URL) { guard let project = self.storage.getProjectBy(url: url.deletingLastPathComponent()) else { return } let state = project.isEncrypted project.isEncrypted = FileManager.default.fileExists(atPath: url.path) DispatchQueue.main.async { if state && !project.isEncrypted { project.password = nil } guard let selectedProject = self.delegate.sidebarOutlineView.getSelectedProject() else { return } self.delegate.sidebarOutlineView.reloadItem(project) // Selected at this moment if selectedProject.url.path == project.url.path { if project.isEncrypted && project.isLocked() { self.delegate.notesTableView.enableLockedProject() self.delegate.editor.clear() } else { self.delegate.notesTableView.disableLockedProject() } self.delegate.updateTable() } } } } ================================================ FILE: FSNotes/Helpers/FileWatcher.swift ================================================ import Cocoa class FileWatcher{ let filePaths: [String] // -- paths to watch - works on folders and file paths var callback : ((_ fileWatcherEvent:FileWatcherEvent) -> Void)? var queue : DispatchQueue? private var streamRef : FSEventStreamRef? private var hasStarted : Bool { return streamRef != nil } init(_ paths:[String]) { self.filePaths = paths } /** * Start listening for FSEvents */ func start() { guard !hasStarted else { return } // -- make sure we are not already listening! var context = FSEventStreamContext( version: 0, info: Unmanaged.passUnretained(self).toOpaque(), retain: retainCallback, release: releaseCallback, copyDescription:nil ) streamRef = FSEventStreamCreate( kCFAllocatorDefault, eventCallback, &context, filePaths as CFArray,FSEventStreamEventId(kFSEventStreamEventIdSinceNow), 0, UInt32(kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagFileEvents) ) selectStreamScheduler() FSEventStreamStart(streamRef!) } /** * Stop listening for FSEvents */ func stop() { guard hasStarted else { return } // -- make sure we are indeed listening! FSEventStreamStop(streamRef!) FSEventStreamInvalidate(streamRef!) FSEventStreamRelease(streamRef!) streamRef = nil } private let eventCallback:FSEventStreamCallback = {( streamRef, clientCallBackInfo, numEvents, eventPaths, eventFlags, eventIds ) in let fileSystemWatcher = Unmanaged.fromOpaque(clientCallBackInfo!).takeUnretainedValue() let paths = Unmanaged.fromOpaque(eventPaths).takeUnretainedValue() as! [String] for index in 0...fromOpaque(info!).retain() return info } private let releaseCallback:CFAllocatorReleaseCallBack = {(info:UnsafeRawPointer?) in Unmanaged.fromOpaque(info!).release() } private func selectStreamScheduler() { guard let streamRef = streamRef else { return } if let queue = queue { FSEventStreamSetDispatchQueue(streamRef, queue) } else { FSEventStreamScheduleWithRunLoop( streamRef, CFRunLoopGetMain(), CFRunLoopMode.defaultMode.rawValue ) } } } extension FileWatcher { convenience init(_ paths:[String], _ callback: @escaping ((_ fileWatcherEvent:FileWatcherEvent) -> Void)) { self.init(paths) self.callback = callback } } ================================================ FILE: FSNotes/Helpers/FileWatcherEvent.swift ================================================ import Foundation /** * PARAM: id: is an id number that the os uses to differentiate between events. * PARAM: path: is the path the change took place. its formated like so: Users/John/Desktop/test/text.txt * PARAM: flag: pertains to the file event type. * EXAMPLE: let url = NSURL(fileURLWithPath: event.path)//<--formats paths to: file:///Users/John/Desktop/test/text.txt * EXAMPLE: Swift.print("fileWatcherEvent.fileChange: " + "\(event.fileChange)") * EXAMPLE: Swift.print("fileWatcherEvent.fileModified: " + "\(event.fileModified)") * EXAMPLE: Swift.print("\t eventId: \(event.id) - eventFlags: \(event.flags) - eventPath: \(event.path)") */ class FileWatcherEvent{ var id:FSEventStreamEventId var path:String var flags: FSEventStreamEventFlags init(_ eventId:FSEventStreamEventId, _ eventPath: String, _ eventFlags: FSEventStreamEventFlags){ self.id = eventId self.path = eventPath self.flags = eventFlags } } /** * The following code is to differentiate between the FSEvent flag types (aka file event types) * NOTE: Be aware that .DS_STORE changes frequently when other files change */ extension FileWatcherEvent{ /*general*/ var fileChange:Bool {return (flags & FSEventStreamEventFlags(kFSEventStreamEventFlagItemIsFile)) != 0} var dirChange:Bool {return (flags & FSEventStreamEventFlags(kFSEventStreamEventFlagItemIsDir)) != 0} /*CRUD*/ var created:Bool {return (flags & FSEventStreamEventFlags(kFSEventStreamEventFlagItemCreated)) != 0} var removed:Bool {return (flags & FSEventStreamEventFlags(kFSEventStreamEventFlagItemRemoved)) != 0} var renamed:Bool {return (flags & FSEventStreamEventFlags(kFSEventStreamEventFlagItemRenamed)) != 0} var modified:Bool {return (flags & FSEventStreamEventFlags(kFSEventStreamEventFlagItemModified)) != 0} } /** * Convenince */ extension FileWatcherEvent{ /*File*/ var fileCreated:Bool {return fileChange && created} var fileRemoved:Bool {return fileChange && removed} var fileRenamed:Bool {return fileChange && renamed} var fileModified:Bool {return fileChange && modified} /*Directory*/ var dirCreated:Bool {return dirChange && created} var dirRemoved:Bool {return dirChange && removed} var dirRenamed:Bool {return dirChange && renamed} var dirModified:Bool {return dirChange && modified} } /** * Simplifies debugging * EXAMPLE: Swift.print(event.description)//Outputs: The file /Users/John/Desktop/test/text.txt was modified */ extension FileWatcherEvent{ var description:String { var result = "The \(fileChange ? "file":"directory") \(self.path) was" if self.created { result += " created" } if self.removed { result += " removed" } if self.renamed { result += " renamed" } if self.modified { result += " modified" } return result } } ================================================ FILE: FSNotes/Helpers/Printer.swift ================================================ // // Printer.swift // FSNotes // // Created by Oleksandr Hlushchenko on 08.08.2024. // Copyright © 2024 Oleksandr Hlushchenko. All rights reserved. // import WebKit @available(macOS 11.0, *) class Printer: NSObject, WKNavigationDelegate, WKScriptMessageHandler { private var indexURL: URL private var pop: NSPrintOperation? private var webView: WKWebView? init(indexURL: URL) { self.indexURL = indexURL super.init() } func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { guard message.name == "contentLoaded" else { return } let printInfo = NSPrintInfo(dictionary: [.paperSize: CGSize(width: 595.28, height: 841.89)]) printInfo.horizontalPagination = .automatic printInfo.verticalPagination = .automatic let margin = 20.0 printInfo.leftMargin = margin printInfo.topMargin = margin printInfo.rightMargin = margin printInfo.bottomMargin = margin printInfo.isVerticallyCentered = true printInfo.isHorizontallyCentered = true self.pop = self.webView?.printOperation(with: printInfo) self.pop?.printPanel.options.insert(.showsPaperSize) self.pop?.printPanel.options.insert(.showsOrientation) self.pop?.printPanel.options.insert(.showsPreview) self.pop?.showsPrintPanel = true self.pop?.showsProgressPanel = true self.pop?.view?.frame = NSRect(x: 0, y: 0, width: 800.0, height: 500.0) DispatchQueue.main.async { let window = NSApplication.shared.mainWindow //let window = NSWindow(contentRect: .zero, styleMask: .borderless, backing: .buffered, defer: false) self.pop?.runModal(for: window!, delegate: nil, didRun: nil, contextInfo: nil) } } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { let checkContentLoadedScript = """ function checkIfComplete() { if (document.readyState === 'complete') { window.webkit.messageHandlers.contentLoaded.postMessage("contentLoaded"); } else { setTimeout(checkIfComplete, 100); } } checkIfComplete(); """ webView.evaluateJavaScript(checkContentLoadedScript, completionHandler: nil) } public func printWeb() { let contentController = WKUserContentController() contentController.add(self, name: "contentLoaded") let config = WKWebViewConfiguration() config.userContentController = contentController webView = WKWebView(frame: NSRect(x: 0, y: 0, width: 595, height: 842), configuration: config) webView?.navigationDelegate = self let accessURL = indexURL.deletingLastPathComponent() webView?.loadFileURL(indexURL, allowingReadAccessTo: accessURL) } } ================================================ FILE: FSNotes/Helpers/PrinterLegacy.swift ================================================ // // PrinterLegacy.swift // FSNotes // // Created by Oleksandr Hlushchenko on 08.08.2024. // Copyright © 2024 Oleksandr Hlushchenko. All rights reserved. // import WebKit @available(*, deprecated, message: "Remove after macOS 10.15 is no longer supported") class PrinterLegacy: NSObject, WebFrameLoadDelegate { private var indexURL: URL private var pop: NSPrintOperation? public var printWebView = WebView() init(indexURL: URL) { self.indexURL = indexURL super.init() } func webView(_ sender: WebView!, didFinishLoadFor frame: WebFrame!) { if sender.isLoading { return } if frame != sender.mainFrame { return } if sender.stringByEvaluatingJavaScript(from: "document.readyState") == "complete" { sender.frameLoadDelegate = nil let printInfo = NSPrintInfo.shared printInfo.paperSize = NSMakeSize(595.22, 841.85) printInfo.topMargin = 40.0 printInfo.leftMargin = 40.0 printInfo.rightMargin = 40.0 printInfo.bottomMargin = 40.0 let when = DispatchTime.now() + 0.2 DispatchQueue.main.asyncAfter(deadline: when) { let operation: NSPrintOperation = NSPrintOperation(view: sender.mainFrame.frameView.documentView, printInfo: printInfo) operation.printPanel.options.insert(NSPrintPanel.Options.showsPaperSize) operation.printPanel.options.insert(NSPrintPanel.Options.showsOrientation) operation.run() } } } public func printWeb() { let printDir = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("Print") guard let content = try? String(contentsOf: indexURL) else { return } self.printWebView.frameLoadDelegate = self self.printWebView.frame = NSRect(x: 0, y: 0, width: 800.0, height: 500.0) self.printWebView.mainFrame.loadHTMLString(content, baseURL: printDir) } } ================================================ FILE: FSNotes/Helpers/SandboxBookmark.swift ================================================ // // SandboxBookmark.swift // FSNotes // // Created by Oleksandr Glushchenko on 8/6/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Foundation class SandboxBookmark { static var instance: SandboxBookmark? = nil var bookmarks = [URL: Data]() var successfullyRestored = [URL]() public static func sharedInstance() -> SandboxBookmark { guard let sandbox = self.instance else { self.instance = SandboxBookmark() return self.instance! } return sandbox } public func resetBookmarksDb() { if let url = bookmark() { try? FileManager.default.removeItem(at: url) } } func bookmark() -> URL? { if var url = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first { url = url.appendingPathComponent("Bookmarks.dict") return url } return nil } func load() { guard let url = bookmark() else { return } if FileManager.default.fileExists(atPath: url.path), let data = try? Data(contentsOf: url) { do { if let bookmarks = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSDictionary.self, NSURL.self, NSData.self], from: data) as? [URL: Data] { self.bookmarks = bookmarks for bookmark in bookmarks { _ = restore(bookmark) } } } catch { print("Failed to unarchive bookmarks: \(error.localizedDescription)") } } } func save() { guard let fileURL = bookmark() else { return } do { let data = try NSKeyedArchiver.archivedData(withRootObject: bookmarks, requiringSecureCoding: false) try data.write(to: fileURL) } catch { print("Failed to save bookmarks: \(error.localizedDescription)") } } func store(url: URL) { #if os(OSX) do { let data = try url.bookmarkData(options: NSURL.BookmarkCreationOptions.withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil) bookmarks[url] = data } catch { Swift.print(error) Swift.print("Error storing bookmarks") } #endif } func restore(_ bookmark: (key: URL, value: Data)) -> Bool { #if os(OSX) let restoredUrl: URL? var isStale = false do { restoredUrl = try URL.init(resolvingBookmarkData: bookmark.value, options: NSURL.BookmarkResolutionOptions.withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale) } catch { Swift.print("Error restoring bookmarks: \(error)") restoredUrl = nil remove(url: bookmark.key) save() } guard let url = restoredUrl else { return false } if isStale { Swift.print("URL is stale: \(url)") return false } if url.startAccessingSecurityScopedResource() { print("Bookmark restored: \(url.path)") successfullyRestored.append(url) return true } Swift.print("Couldn't access: \(url.path)") #endif return false } func remove(url: URL) { bookmarks.removeValue(forKey: url) } func removeBy(_ url: URL) { load() bookmarks.removeValue(forKey: url) save() } func rename(url: URL, new: URL) { let value = bookmarks[url] bookmarks[new] = value save() } public func save(url: URL) { guard let fileURL = bookmark() else { return } if self.bookmarks.isEmpty, FileManager.default.fileExists(atPath: fileURL.path), let data = try? Data(contentsOf: fileURL) { do { if let bookmarks = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSDictionary.self, NSURL.self, NSData.self], from: data) as? [URL: Data] { self.bookmarks = bookmarks } } catch { print("Failed to unarchive bookmarks: \(error.localizedDescription)") } } self.store(url: url) self.save() } public func getRestoredUrls() -> [URL] { if successfullyRestored.isEmpty { load() } return successfullyRestored } } ================================================ FILE: FSNotes/Helpers/Sidebar.swift ================================================ // // Sidebar.swift // FSNotes // // Created by Oleksandr Glushchenko on 4/7/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa typealias Image = NSImage class Sidebar { var list = [Any]() let storage = Storage.shared() public var items = [[SidebarItem]]() init() { guard let defaultURL = Storage.shared().getDefault()?.url else { return } var system = [SidebarItem]() if UserDefaultsManagement.sidebarVisibilityNotes { // Notes guard let defaultURL = Storage.shared().getDefault()?.url else { return } let notesUrl = defaultURL.appendingPathComponent("Fake Virtual Notes Dir") let notesLabel = NSLocalizedString("Notes", comment: "") let fakeNotesProject = Project( storage: Storage.shared(), url: notesUrl, label: notesLabel, isVirtual: true ) let notes = SidebarItem(name: NSLocalizedString("Notes", comment: ""), project: fakeNotesProject, type: .All) system.append(notes) Storage.shared().allNotesProject = fakeNotesProject } if UserDefaultsManagement.sidebarVisibilityInbox { let project = Storage.shared().getDefault() let notes = SidebarItem(name: NSLocalizedString("Inbox", comment: ""), project: project, type: .Inbox) system.append(notes) } if UserDefaultsManagement.sidebarVisibilityTodo { let todoUrl = defaultURL.appendingPathComponent("Fake Virtual Todo Dir") let todoLabel = NSLocalizedString("Todo", comment: "") let fakeTodoProject = Project( storage: Storage.shared(), url: todoUrl, label: todoLabel, isVirtual: true ) let todo = SidebarItem(name: NSLocalizedString("Todo", comment: ""), project: fakeTodoProject, type: .Todo) system.append(todo) Storage.shared().todoProject = fakeTodoProject } if UserDefaultsManagement.sidebarVisibilityUntagged { let todoUrl = defaultURL.appendingPathComponent("Fake Virtual Utagged Dir") let untaggedLabel = NSLocalizedString("Untagged", comment: "") let fakeUntaggedProject = Project( storage: Storage.shared(), url: todoUrl, label: untaggedLabel, isVirtual: true ) let todo = SidebarItem(name: NSLocalizedString("Untagged", comment: ""), project: fakeUntaggedProject, type: .Untagged) system.append(todo) Storage.shared().untaggedProject = fakeUntaggedProject } if UserDefaultsManagement.sidebarVisibilityTrash { let trashProject = Storage.shared().getDefaultTrash() let trash = SidebarItem(name: NSLocalizedString("Trash", comment: ""), project: trashProject, type: .Trash) system.append(trash) } if system.count > 0 { list = system } list.append(SidebarItem(name: "projects", type: .Separator)) let projects = storage.getSidebarProjects() if projects.count > 0 { for project in projects { list.append(project) } } list.append(SidebarItem(name: "tags", type: .Separator)) } public func getList() -> [Any] { return list } private func getDefaultLabelName(project: Project) -> String { var name = project.label let iCloudPath = "/Users/\(NSUserName())/Library/Mobile Documents" if project.url.path.starts(with: iCloudPath) { name = NSLocalizedString("iCloud Drive", comment: "") } let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.path if let path = documentsPath, project.url.path.starts(with: path) { name = NSLocalizedString("Documents", comment: "") } return name } } ================================================ FILE: FSNotes/Helpers/UserDataService.swift ================================================ // // UserDataService.swift // FSNotes // // Created by Oleksandr Glushchenko on 1/30/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation #if os(iOS) import UIKit #endif public class UserDataService { public static let instance = UserDataService() fileprivate var _searchTrigger = false fileprivate var _lastRenamed: URL? fileprivate var _isNotesTableEscape = false fileprivate var _isDark = false fileprivate var _lastType: Int? fileprivate var _lastProject: URL? fileprivate var _lastName: String? fileprivate var _importProgress = false public var searchTrigger: Bool { get { return _searchTrigger } set { _searchTrigger = newValue } } public var focusOnImport: URL? { get { return _lastRenamed } set { _lastRenamed = newValue } } public var isNotesTableEscape: Bool { get { return _isNotesTableEscape } set { _isNotesTableEscape = newValue } } public var isDark: Bool { get { #if os(iOS) return UITraitCollection.current.userInterfaceStyle == .dark #else return _isDark #endif } set { _isDark = newValue } } public var lastType: Int? { get { return _lastType } set { _lastType = newValue } } public var lastName: String? { get { return _lastName } set { _lastName = newValue } } public var lastProject: URL? { get { return _lastProject } set { _lastProject = newValue } } public func resetLastSidebar() { _lastProject = nil _lastType = nil _lastName = nil } public var skipSidebarSelection: Bool { get { return _importProgress } set { _importProgress = newValue } } } ================================================ FILE: FSNotes/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "filename" : "icon_16x16.png", "idiom" : "mac", "scale" : "1x", "size" : "16x16" }, { "filename" : "icon_16x16@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "16x16" }, { "filename" : "icon_32x32.png", "idiom" : "mac", "scale" : "1x", "size" : "32x32" }, { "filename" : "icon_32x32@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "32x32" }, { "filename" : "icon_128x128.png", "idiom" : "mac", "scale" : "1x", "size" : "128x128" }, { "filename" : "icon_128x128@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "128x128" }, { "filename" : "icon_256x256.png", "idiom" : "mac", "scale" : "1x", "size" : "256x256" }, { "filename" : "icon_256x256@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { "filename" : "icon_512x512.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { "filename" : "icon_512x512@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/Icons/AppIconClassic.imageset/Contents.json ================================================ { "images" : [ { "filename" : "icon_alt.png", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/Icons/AppIconModern.imageset/Contents.json ================================================ { "images" : [ { "filename" : "icon.png", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/Icons/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/checkbox_empty.imageset/Contents.json ================================================ { "images" : [ { "filename" : "checkbox_empty.png", "idiom" : "universal", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "checkbox_empty_white.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "checkbox_empty@2x.png", "idiom" : "universal", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "checkbox_empty_white@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "checkbox_empty@3x.png", "idiom" : "universal", "scale" : "3x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "checkbox_empty_white@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/checkbox_flipped.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "checkbox_flipped.png", "scale" : "1x" }, { "idiom" : "universal", "filename" : "checkbox_flipped_white.png", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "scale" : "1x" }, { "idiom" : "universal", "filename" : "checkbox_flipped@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "checkbox_flipped_white@2x.png", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "scale" : "2x" }, { "idiom" : "universal", "filename" : "checkbox_flipped@3x.png", "scale" : "3x" }, { "idiom" : "universal", "scale" : "3x", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ] } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: FSNotes/Images.xcassets/checkbox_new.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "checkbox.png", "scale" : "1x" }, { "idiom" : "universal", "filename" : "checkbox_white.png", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "scale" : "1x" }, { "idiom" : "universal", "filename" : "checkbox@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "checkbox_white@2x.png", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "scale" : "2x" }, { "idiom" : "universal", "filename" : "checkbox@3x.png", "scale" : "3x" }, { "idiom" : "universal", "filename" : "checkbox_white@3x.png", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: FSNotes/Images.xcassets/code.colorset/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" }, "colors" : [ { "idiom" : "universal", "color" : { "color-space" : "srgb", "components" : { "red" : "0.940", "alpha" : "1.000", "blue" : "0.950", "green" : "0.950" } } }, { "idiom" : "universal", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "red" : "0.270", "alpha" : "1.000", "blue" : "0.270", "green" : "0.270" } } } ] } ================================================ FILE: FSNotes/Images.xcassets/colors_background/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/colors_background/background_tag.colorset/Contents.json ================================================ { "colors" : [ { "color" : { "color-space" : "srgb", "components" : { "alpha" : "1.000", "blue" : "0xEA", "green" : "0xA8", "red" : "0x78" } }, "idiom" : "universal" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "alpha" : "1.000", "blue" : "0x63", "green" : "0x84", "red" : "0x53" } }, "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/colors_background/background_win.colorset/Contents.json ================================================ { "colors" : [ { "color" : { "color-space" : "srgb", "components" : { "alpha" : "1.000", "blue" : "0xFF", "green" : "0xFF", "red" : "0xFF" } }, "idiom" : "universal" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "alpha" : "1.000", "blue" : "0x23", "green" : "0x20", "red" : "0x20" } }, "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/copy.png.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "copy-2.png", "scale" : "1x" }, { "idiom" : "universal", "filename" : "copy_white-1.png", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "scale" : "1x" }, { "idiom" : "universal", "filename" : "copy-1.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "copy_white.png", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "scale" : "2x" }, { "idiom" : "universal", "filename" : "copy.png", "scale" : "3x" }, { "idiom" : "universal", "scale" : "3x", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ] } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: FSNotes/Images.xcassets/divider.colorset/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" }, "colors" : [ { "idiom" : "universal", "color" : { "color-space" : "srgb", "components" : { "red" : "0xE9", "alpha" : "1.000", "blue" : "0xE9", "green" : "0xE9" } } }, { "idiom" : "universal", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "red" : "0x00", "alpha" : "1.000", "blue" : "0x00", "green" : "0x00" } } } ] } ================================================ FILE: FSNotes/Images.xcassets/dockIcon2.imageset/Contents.json ================================================ { "images" : [ { "filename" : "image@1x.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "image@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "image@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/dockIcon4.imageset/Contents.json ================================================ { "images" : [ { "filename" : "icon-64.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "icon-128.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "icon-256.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/friend.imageset/Contents.json ================================================ { "images" : [ { "filename" : "friend.png", "idiom" : "mac", "scale" : "1x" }, { "filename" : "friend 1.png", "idiom" : "mac", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/highlight.colorset/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" }, "colors" : [ { "idiom" : "universal", "color" : { "color-space" : "srgb", "components" : { "red" : "1.000", "alpha" : "1.000", "blue" : "0.700", "green" : "0.900" } } }, { "idiom" : "universal", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "red" : "0.200", "alpha" : "1.000", "blue" : "0.070", "green" : "0.550" } } } ] } ================================================ FILE: FSNotes/Images.xcassets/link.colorset/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" }, "colors" : [ { "idiom" : "universal", "color" : { "color-space" : "srgb", "components" : { "red" : "0.240", "alpha" : "1.000", "blue" : "0.890", "green" : "0.510" } } }, { "idiom" : "universal", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "red" : "0.490", "alpha" : "1.000", "blue" : "0.630", "green" : "0.920" } } } ] } ================================================ FILE: FSNotes/Images.xcassets/locked.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "locked.png", "scale" : "1x" }, { "idiom" : "universal", "filename" : "locked-1.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "locked-2.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: FSNotes/Images.xcassets/mainBackground.colorset/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" }, "colors" : [ { "idiom" : "universal", "color" : { "color-space" : "srgb", "components" : { "red" : "0xFF", "alpha" : "1.000", "blue" : "0xFF", "green" : "0xFF" } } }, { "idiom" : "universal", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "red" : "0x2A", "alpha" : "1.000", "blue" : "0x2E", "green" : "0x2B" } } } ] } ================================================ FILE: FSNotes/Images.xcassets/mainText.colorset/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" }, "colors" : [ { "idiom" : "universal", "color" : { "color-space" : "srgb", "components" : { "red" : "0.000", "alpha" : "1.000", "blue" : "0.000", "green" : "0.000" } } }, { "idiom" : "universal", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "red" : "1.000", "alpha" : "1.000", "blue" : "1.000", "green" : "1.000" } } } ] } ================================================ FILE: FSNotes/Images.xcassets/menuBar.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "icon-simple-bw-b-9-512x512@2x-2.png", "scale" : "1x" }, { "idiom" : "universal", "filename" : "icon-simple-bw-w-9-512x512@2x.png", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "scale" : "1x" }, { "idiom" : "universal", "filename" : "icon-simple-bw-b-9-512x512@2x-1.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "icon-simple-bw-w-9-512x512@2x-1.png", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "scale" : "2x" }, { "idiom" : "universal", "filename" : "icon-simple-bw-b-9-512x512@2x.png", "scale" : "3x" }, { "idiom" : "universal", "filename" : "icon-simple-bw-w-9-512x512@2x-2.png", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: FSNotes/Images.xcassets/new_note_button.imageset/Contents.json ================================================ { "images" : [ { "filename" : "new_note-76.png", "idiom" : "mac", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "new_note-77.png", "idiom" : "mac", "scale" : "1x" }, { "filename" : "new_note-76@2x.png", "idiom" : "mac", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "new_note-76@2x-1.png", "idiom" : "mac", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/pin.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "pin.png", "scale" : "1x" }, { "idiom" : "universal", "filename" : "pin_white.png", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "scale" : "1x" }, { "idiom" : "universal", "filename" : "pin-1.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "pin_white-1.png", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "scale" : "2x" }, { "idiom" : "universal", "filename" : "pin-2.png", "scale" : "3x" }, { "idiom" : "universal", "scale" : "3x", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ] } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: FSNotes/Images.xcassets/prefsAdvanced.imageset/Contents.json ================================================ { "images" : [ { "filename" : "image@1x.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "image@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "image@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/prefsEditor.imageset/Contents.json ================================================ { "images" : [ { "filename" : "editor.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "editor@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "editor@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/prefsGeneral.imageset/Contents.json ================================================ { "images" : [ { "filename" : "image@1x.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "image@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "image@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/prefsGit.imageset/Contents.json ================================================ { "images" : [ { "filename" : "git.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "git@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "git@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/prefsLayout.imageset/Contents.json ================================================ { "images" : [ { "filename" : "layout.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "layout@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "layout@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/prefsWeb.imageset/Contents.json ================================================ { "images" : [ { "filename" : "prefsWeb-64.png", "idiom" : "mac", "scale" : "1x" }, { "filename" : "prefsWeb-128.png", "idiom" : "mac", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/privacy.imageset/Contents.json ================================================ { "images" : [ { "filename" : "privacy.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "privacy@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "privacy@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/quoteColor.colorset/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" }, "colors" : [ { "idiom" : "universal", "color" : { "color-space" : "srgb", "components" : { "red" : "0x81", "alpha" : "1.000", "blue" : "0x81", "green" : "0x81" } } }, { "idiom" : "universal", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "red" : "0x80", "alpha" : "1.000", "blue" : "0x80", "green" : "0x80" } } } ] } ================================================ FILE: FSNotes/Images.xcassets/reverseBackground.colorset/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" }, "colors" : [ { "idiom" : "universal", "color" : { "color-space" : "srgb", "components" : { "red" : "0x2A", "alpha" : "1.000", "blue" : "0x2E", "green" : "0x2B" } } }, { "idiom" : "universal", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "red" : "0xFF", "alpha" : "1.000", "blue" : "0xFF", "green" : "0xFF" } } } ] } ================================================ FILE: FSNotes/Images.xcassets/sidebar_archive.imageset/Contents.json ================================================ { "images" : [ { "filename" : "Archive-76.png", "idiom" : "mac", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "Archive-77.png", "idiom" : "mac", "scale" : "1x" }, { "filename" : "Archive-76@2x-1.png", "idiom" : "mac", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "Archive-76@2x.png", "idiom" : "mac", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 }, "properties" : { "localizable" : true } } ================================================ FILE: FSNotes/Images.xcassets/sidebar_external.imageset/Contents.json ================================================ { "images" : [ { "filename" : "185097_database_icon-32.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "185097_database_icon-64.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "185097_database_icon-128.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/sidebar_icloud_drive.imageset/Contents.json ================================================ { "images" : [ { "filename" : "1820466_brand_icloud_logo_network_social_icon-32.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "1820466_brand_icloud_logo_network_social_icon-64.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "1820466_brand_icloud_logo_network_social_icon-128.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/sidebar_inbox.imageset/Contents.json ================================================ { "images" : [ { "filename" : "1172241_inbox_letter_mail_mailbox_icon-76.png", "idiom" : "universal", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "1172241_inbox_letter_mail_mailbox_icon-77.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "1172241_inbox_letter_mail_mailbox_icon-76@2x.png", "idiom" : "universal", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "1172241_inbox_letter_mail_mailbox_icon-76@2x-1.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "1172241_inbox_letter_mail_mailbox_icon-83.5@2x.png", "idiom" : "universal", "scale" : "3x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "1172241_inbox_letter_mail_mailbox_icon-83.5@2x-1.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/sidebar_notes.imageset/Contents.json ================================================ { "images" : [ { "filename" : "Notes-77.png", "idiom" : "mac", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "Notes-76.png", "idiom" : "mac", "scale" : "1x" }, { "filename" : "Notes-76@2x-1.png", "idiom" : "mac", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "Notes-76@2x.png", "idiom" : "mac", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/sidebar_project.imageset/Contents.json ================================================ { "images" : [ { "filename" : "folder-76.png", "idiom" : "mac", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "folder-77.png", "idiom" : "mac", "scale" : "1x" }, { "filename" : "folder-76@2x.png", "idiom" : "mac", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "folder-76@2x-1.png", "idiom" : "mac", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/sidebar_project_encrypted_locked.imageset/Contents.json ================================================ { "images" : [ { "filename" : "Folder3lock4 3.png", "idiom" : "mac", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "Folder3lock4 3-2.png", "idiom" : "mac", "scale" : "1x" }, { "filename" : "Folder3lock4 3-1.png", "idiom" : "mac", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "Folder3lock4 3-3.png", "idiom" : "mac", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/sidebar_project_encrypted_unlocked.imageset/Contents.json ================================================ { "images" : [ { "filename" : "Folder_unlock_1.png", "idiom" : "mac", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "Folder_unlock_1-2.png", "idiom" : "mac", "scale" : "1x" }, { "filename" : "Folder_unlock_1-1.png", "idiom" : "mac", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "Folder_unlock_1-3.png", "idiom" : "mac", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/sidebar_tag.imageset/Contents.json ================================================ { "images" : [ { "filename" : "tag-76.png", "idiom" : "mac", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "tag-77.png", "idiom" : "mac", "scale" : "1x" }, { "filename" : "tag-76@2x.png", "idiom" : "mac", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "tag-76@2x-1.png", "idiom" : "mac", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/sidebar_todo.imageset/Contents.json ================================================ { "images" : [ { "filename" : "t2-77.png", "idiom" : "universal", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "t2-76.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "t2-76@2x-1.png", "idiom" : "universal", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "t2-76@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "t2-83.5@2x-1.png", "idiom" : "universal", "scale" : "3x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "t2-83.5@2x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/sidebar_trash.imageset/Contents.json ================================================ { "images" : [ { "filename" : "Trash-77.png", "idiom" : "universal", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "Trash-76.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "Trash-76@2x-1.png", "idiom" : "universal", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "Trash-76@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "Trash-83.5@2x-1.png", "idiom" : "universal", "scale" : "3x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "Trash-83.5@2x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/sidebar_untagged.imageset/Contents.json ================================================ { "images" : [ { "filename" : "Untagged-76.png", "idiom" : "mac", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "Untagged-77.png", "idiom" : "mac", "scale" : "1x" }, { "filename" : "Untagged-76@2x.png", "idiom" : "mac", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "Untagged-76@2x-1.png", "idiom" : "mac", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Images.xcassets/web.imageset/Contents.json ================================================ { "images" : [ { "filename" : "web.png", "idiom" : "mac", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "web 2.png", "idiom" : "mac", "scale" : "1x" }, { "filename" : "web 1.png", "idiom" : "mac", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "web 3.png", "idiom" : "mac", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes/Info.plist ================================================ ATSApplicationFontsPath . CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName FSNotes CFBundleDocumentTypes CFBundleTypeExtensions etp CFBundleTypeIconFile EncryptedTextPack CFBundleTypeMIMETypes CFBundleTypeName Encrypted Text Pack CFBundleTypeRole Editor LSHandlerRank Owner LSItemContentTypes es.fsnot.etp.package LSTypeIsPackage 0 NSDocumentClass CFBundleTypeExtensions textbundle CFBundleTypeIconFile TextBundle CFBundleTypeName TextBundle CFBundleTypeRole Editor LSHandlerRank Default LSItemContentTypes org.textbundle.package LSTypeIsPackage 1 NSDocumentClass CFBundleTypeExtensions md markdown CFBundleTypeIconFile Markdown CFBundleTypeName Markdown CFBundleTypeRole Editor LSHandlerRank Default LSItemContentTypes net.daringfireball.markdown CFBundleTypeExtensions rtf CFBundleTypeIconFile RTF CFBundleTypeName Rich Text CFBundleTypeRole Editor LSHandlerRank Alternate LSItemContentTypes public.rtf CFBundleTypeExtensions txt plain-text CFBundleTypeIconFile Text CFBundleTypeName Text CFBundleTypeRole Editor LSHandlerRank Alternate LSItemContentTypes public.plain-text CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName FSNotes CFBundlePackageType APPL CFBundleShortVersionString $(MARKETING_VERSION) CFBundleURLTypes CFBundleTypeRole Viewer CFBundleURLIconFile icon CFBundleURLName co.fluder.fsnotes CFBundleURLSchemes fsnotes CFBundleTypeRole Viewer CFBundleURLIconFile icon CFBundleURLName co.fluder.fsnotes CFBundleURLSchemes nv CFBundleTypeRole Viewer CFBundleURLIconFile icon CFBundleURLName co.fluder.fsnotes CFBundleURLSchemes nvalt CFBundleVersion $(CURRENT_PROJECT_VERSION) LSApplicationCategoryType public.app-category.productivity LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement NSAppTransportSecurity NSAllowsArbitraryLoads NSHumanReadableCopyright Copyright © 2017-2022 Oleksandr Hlushchenko. All rights reserved. NSMainStoryboardFile Main NSPrincipalClass NSApplication NSUbiquitousContainers iCloud.co.fluder.fsnotes NSUbiquitousContainerIsDocumentScopePublic NSUbiquitousContainerName FSNotes NSUbiquitousContainerSupportedFolderLevels One NSUserActivityTypes es.fsnot.handoff-open-note UTExportedTypeDeclarations UTTypeConformsTo public.data UTTypeDescription Encrypted Text Pack UTTypeIconFile EncryptedTextPack UTTypeIdentifier es.fsnot.etp.package UTTypeReferenceURL https://fsnot.es UTTypeTagSpecification public.filename-extension etp public.mime-type UTTypeConformsTo com.apple.package UTTypeDescription TextBundle UTTypeIconFile TextBundle UTTypeIdentifier org.textbundle.package UTTypeReferenceURL http://www.textbundle.org UTTypeTagSpecification public.filename-extension textbundle UTTypeConformsTo public.plain-text UTTypeDescription Markdown UTTypeIconFile Markdown UTTypeIdentifier net.daringfireball.markdown UTTypeTagSpecification public.filename-extension md markdown public.mime-type text/markdown UTTypeConformsTo public.text UTTypeDescription Plain Text UTTypeIconFile Text UTTypeIdentifier public.plain-text UTTypeTagSpecification public.filename-extension txt public.mime-type text/plain UTTypeConformsTo public.text UTTypeDescription Rich Text UTTypeIconFile RTF UTTypeIdentifier public.rtf UTTypeTagSpecification public.filename-extension rtf public.mime-type text/rtf ================================================ FILE: FSNotes/LayoutManager.swift ================================================ // // CustomLayoutManager.swift // FSNotes // // Created by Oleksandr Hlushchenko on 24.08.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import Cocoa fileprivate extension NSRange { /// Clamp range to fit inside given maxRange func clamped(to maxRange: NSRange) -> NSRange { if maxRange.length == 0 { return NSRange(location: maxRange.location, length: 0) } if self.location >= NSMaxRange(maxRange) { return NSRange(location: NSMaxRange(maxRange), length: 0) } let start = max(self.location, maxRange.location) let end = min(NSMaxRange(self), NSMaxRange(maxRange)) if end <= start { return NSRange(location: start, length: 0) } return NSRange(location: start, length: end - start) } } class LayoutManager: NSLayoutManager, NSLayoutManagerDelegate { weak var processor: TextStorageProcessor? override init() { super.init() self.allowsNonContiguousLayout = true } required init?(coder: NSCoder) { super.init(coder: coder) self.allowsNonContiguousLayout = true } public var lineHeightMultiple: CGFloat = CGFloat(UserDefaultsManagement.lineHeightMultiple) private var defaultFont: NSFont { return self.firstTextView?.font ?? NSFont.systemFont(ofSize: NSFont.systemFontSize) } private func font(for glyphRange: NSRange) -> NSFont { guard let textStorage = self.textStorage else { return defaultFont } let characterRange = self.characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil) let storageRange = NSRange(location: 0, length: textStorage.length) let safeCharRange = characterRange.clamped(to: storageRange) guard safeCharRange.length > 0 else { return defaultFont } let attributes = textStorage.attributes(at: safeCharRange.location, effectiveRange: nil) return attributes[.font] as? NSFont ?? defaultFont } private func hasAttachment(in glyphRange: NSRange) -> (hasAttachment: Bool, maxAttachmentHeight: CGFloat) { guard let textStorage = self.textStorage else { return (false, 0) } let characterRange = self.characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil) let storageRange = NSRange(location: 0, length: textStorage.length) let safeCharRange = characterRange.clamped(to: storageRange) if safeCharRange.length == 0 { return (false, 0) } var maxHeight: CGFloat = 0 var hasAttachment = false textStorage.enumerateAttribute(.attachment, in: safeCharRange, options: []) { value, _, _ in if let attachment = value as? NSTextAttachment { hasAttachment = true let attachmentBounds = attachment.bounds maxHeight = max(maxHeight, attachmentBounds.height) } } return (hasAttachment, maxHeight) } public func lineHeight(for font: NSFont) -> CGFloat { let fontLineHeight = self.defaultLineHeight(for: font) let lineHeight = fontLineHeight * lineHeightMultiple return lineHeight } private func isInCodeBlock(characterIndex: Int) -> Bool { guard let textStorage = self.textStorage else { return false } let ns = textStorage.string as NSString let storageFullRange = NSRange(location: 0, length: ns.length) if characterIndex < 0 || characterIndex >= NSMaxRange(storageFullRange) { return false } guard let codeBlocks = processor?.editor?.note?.codeBlockRangesCache else { return false } return codeBlocks.contains { NSLocationInRange(characterIndex, $0) } } // MARK: - Drawing override func drawBackground(forGlyphRange glyphsToShow: NSRange, at origin: CGPoint) { drawCodeBlockBackground(forGlyphRange: glyphsToShow, at: origin) super.drawBackground(forGlyphRange: glyphsToShow, at: origin) } override func fillBackgroundRectArray(_ rectArray: UnsafePointer, count rectCount: Int, forCharacterRange charRange: NSRange, color: NSColor) { let storageLength = self.textStorage?.length ?? 0 let storageFullRange = NSRange(location: 0, length: storageLength) let safeCharRange = charRange.clamped(to: storageFullRange) if color == NSColor.selectedTextBackgroundColor || color == NSColor.unemphasizedSelectedTextBackgroundColor || !isInCodeBlock(characterIndex: safeCharRange.location) { super.fillBackgroundRectArray(rectArray, count: rectCount, forCharacterRange: charRange, color: color) } } private func drawCodeBlockBackground(forGlyphRange glyphsToShow: NSRange, at origin: CGPoint) { guard let textStorage = self.textStorage, let context = NSGraphicsContext.current?.cgContext else { return } let storageFullRange = NSRange(location: 0, length: textStorage.length) guard let allCodeBlocks = processor?.editor?.note?.codeBlockRangesCache else { return } guard let textContainer = self.textContainers.first else { return } let visibleCharRange = self.characterRange(forGlyphRange: glyphsToShow, actualGlyphRange: nil) let relevantCodeBlocks = allCodeBlocks.filter { codeBlock in NSIntersectionRange(codeBlock, visibleCharRange).length > 0 } guard !relevantCodeBlocks.isEmpty else { return } textContainer.lineFragmentPadding = 10 context.saveGState() let backgroundColor = NotesTextProcessor.getHighlighter().options.style.backgroundColor.cgColor let borderColor = NSColor.lightGray.cgColor for codeBlockRange in relevantCodeBlocks { // ← теперь только релевантные блоки! let safeCharRange = codeBlockRange.clamped(to: storageFullRange) if safeCharRange.length == 0 { continue } let glyphRange = self.glyphRange(forCharacterRange: safeCharRange, actualCharacterRange: nil) if glyphRange.length == 0 { continue } let boundingRect = self.boundingRect(forGlyphRange: glyphRange, in: textContainer) if boundingRect.isEmpty { continue } // Padding left/right let horizontalPadding: CGFloat = 5.0 let paddedRect = boundingRect .insetBy(dx: -horizontalPadding, dy: 0) .offsetBy(dx: origin.x, dy: origin.y) // Round borders let radius: CGFloat = 5.0 let path = CGPath(roundedRect: paddedRect, cornerWidth: radius, cornerHeight: radius, transform: nil) context.setFillColor(backgroundColor) context.addPath(path) context.fillPath() // Border 1px context.addPath(path) context.setStrokeColor(borderColor) context.setLineWidth(1.0) context.strokePath() } context.restoreGState() } public func layoutManager( _ layoutManager: NSLayoutManager, shouldSetLineFragmentRect lineFragmentRect: UnsafeMutablePointer, lineFragmentUsedRect: UnsafeMutablePointer, baselineOffset: UnsafeMutablePointer, in textContainer: NSTextContainer, forGlyphRange glyphRange: NSRange) -> Bool { // Get the font for the current range of glyphs let currentFont = font(for: glyphRange) let fontLineHeight = layoutManager.defaultLineHeight(for: currentFont) let standardLineHeight = fontLineHeight * lineHeightMultiple let attachmentInfo = hasAttachment(in: glyphRange) var finalLineHeight: CGFloat var baselineNudge: CGFloat if attachmentInfo.hasAttachment && attachmentInfo.maxAttachmentHeight > 0 { if attachmentInfo.maxAttachmentHeight > standardLineHeight { finalLineHeight = attachmentInfo.maxAttachmentHeight baselineNudge = 0 } else { finalLineHeight = standardLineHeight let extraSpace = finalLineHeight - fontLineHeight baselineNudge = extraSpace * 0.5 } } else { finalLineHeight = standardLineHeight let extraSpace = finalLineHeight - fontLineHeight baselineNudge = extraSpace * 0.5 } var rect = lineFragmentRect.pointee rect.size.height = ceil(finalLineHeight) var usedRect = lineFragmentUsedRect.pointee usedRect.size.height = max(rect.size.height, ceil(usedRect.size.height)) lineFragmentRect.pointee = rect lineFragmentUsedRect.pointee = usedRect baselineOffset.pointee = baselineOffset.pointee + baselineNudge return true } func refreshLayoutSoftly() { invalidateLayout(forCharacterRange: NSRange(location: 0, length: textStorage?.length ?? 0), actualCharacterRange: nil) textContainers.forEach { container in container.textView?.needsDisplay = true } } override func setExtraLineFragmentRect( _ fragmentRect: NSRect, usedRect: NSRect, textContainer container: NSTextContainer) { var fontToUse: NSFont if let textStorage = self.textStorage, textStorage.length > 0 { let lastIndex = textStorage.length - 1 let attributes = textStorage.attributes(at: lastIndex, effectiveRange: nil) let nsString = textStorage.string as NSString let lastCharIsNewline = nsString.character(at: lastIndex) == 0x0A // '\n' if !lastCharIsNewline, let font = attributes[.font] as? NSFont { fontToUse = font } else { fontToUse = UserDefaultsManagement.noteFont } } else { fontToUse = UserDefaultsManagement.noteFont } let lineHeight = self.lineHeight(for: fontToUse) var fragmentRect = fragmentRect fragmentRect.size.height = ceil(lineHeight) var usedRect = usedRect usedRect.size.height = ceil(lineHeight) super.setExtraLineFragmentRect(fragmentRect, usedRect: usedRect, textContainer: container) } } ================================================ FILE: FSNotes/Localizable.xcstrings ================================================ { "sourceLanguage" : "en", "strings" : { "Add" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "يضيف" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přidat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Hinzufügen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Añadir" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ajouter" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הוסף" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "जोड़ें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Aggiungi" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "追加" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "추가" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toevoegen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Adicionar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Adicionar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Добавить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ekle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Додати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "添加" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "新增" } } } }, "Add External Folder..." : { "comment" : "Menu Library", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ارفاق تخزين..." } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přidat externí složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Speicher anschließen..." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Adjuntar almacenamiento..." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Attacher un stockage..." } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הוסף תיקיה חיצונית..." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बाह्य फ़ोल्डर जोड़ें..." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Collega archivio..." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "外部フォルダを追加…" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "저장소 추가..." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Koppelen opslag..." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Adicionar pasta externa..." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Adicionar armazenamento..." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Подключить хранилище..." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Harici Klasör Ekle..." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Підключити сховище..." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "附件存储..." } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "附件存檔" } } } }, "Are you really want to remove %d tag(s)? This action can not be undone." : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "هل تريد حقًا إزالة٪ d علامة (علامات)؟ لا يمكن التراجع عن هذا الإجراء." } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Opravdu chcete odstranit tyto značky (%d)? Tuto akci nelze odvolat." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Möchten Sie wirklich %d Tag(s) entfernen? Diese Aktion kann nicht rückgängig gemacht werden." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "¿Seguro que quieres borrar %d etiqueta/s? Esta operación no se puede deshacer." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Êtes-vous sûr de vouloir supprimer %d étiquette(s) ? Cette action est irréversible." } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "האם ברצונך להסיר %d תג(ים)? לא ניתן לבטל פעולה זו." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्या आप वाकई %d टैग हटाना चाहते हैं? यह क्रिया पूर्ववत नहीं की जा सकती।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Vuoi veramente rimuovere %d tag(s)? Questa azione non può essere annullata." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "本当にこれら%d個のタグを削除しますか?一度削除したら元に戻すことはできません" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Are you really want to remove %d tag(s)? This action can not be undone." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Are you really want to remove %d tag(s)? This action can not be undone." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tem certeza que deseja remover %d tag(s)? Essa ação não pode ser desfeita." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tem a certeza que pretende remover %d etiqueta(s)? Esta acção é irreversível." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вы действительно хотите удалить %d тег(ов)? Это действие не может быть отменено." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Gerçekten %d etiketi kaldırmak istiyor musunuz? Bu işlem geri alınamaz." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ви дійсно хочете видалити %d тег(и)? Ця дія не може бути скасована." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "您真的要删除 %d 个标签吗?此操作无法撤消。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "確定要刪除 %d 個標籤嗎?此動作無法復原" } } } }, "Are you sure you want to irretrievably delete %d note(s)?" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "هل انت متأكد من حذف الملفات %d بشكل نهائي ؟" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Určitě chcete nenávratně smazat tyto poznámky (%d)?" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sind Sie sicher, dass Sie %d Notiz(en) unwiderruflich löschen möchten?" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "¿Seguro que quieres borrar definitivamente %d notas?" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Êtes-vous sûr de vouloir supprimer définitivement %d note(s) ?" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "האם ברצונך למחוק %d פתק(ים) באופן בלתי הפיך?" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्या आप वाकई %d नोट को पूरी तरह से हटाना चाहते हैं?" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sei sicuro di voler eliminare in modo irreversibile la/e nota/e %d ?" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "これら%d件のノートを本当に削除してもいいですか?(もとに戻せません)" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "%d개의 노트를 영구적으로 지우겠습니까?" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Weet je zeker dat je %d notitie(s) definitief wilt wissen?" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tem certeza que deseja deletar as notas %d irreversivelmente?" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tem a certeza que pretende eliminar %d nota(s) de forma irreversível?" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вы уверены что хотите выполнить необратимое удаление %d заметки(ок)?" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "%d notu geri alınamaz şekilde silmek istediğinizden emin misiniz?" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вы впевнені що хочете назавжди видалити %d нотатку(ок)? " } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "确定要永久删除%d个笔记?此操作无法恢复!" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "確定要永久刪除 %d 個筆記嗎?此操作無法復原!" } } } }, "Are you sure you want to remove project \"%@\" and all files inside?" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "هل انت متأكد من حذف المشروع \"%@\" وكل المفات التي بداخله" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Opravdu chcete odebrat projekt „%@“ včetně všech souborů uvnitř?" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sind Sie sicher, dass Sie Projekt \"%@\" löschen möchten?" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "¿Seguro que quieres borrar el proyecto \"%@\" con todo su contenido?" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Êtes-vous sûr de vouloir supprimer le projet \"%@\" et tous ses fichiers ?" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "האם ברצונך להסיר פרוייקט \"%@\" ואת כל הקבצים שבתוכו?" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्या आप वाकई प्रोजेक्ट \"%@\" और उसके अंदर की सभी फाइलें हटाना चाहते हैं?" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sei sicuro di voler eliminare il progetto \"%@\" e tutti i file al suo interno?" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "プロジェクト \"%@\" と中に入っているすべてのノートを本当に削除してもいいですか?" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "프로젝트 \"%@\"를 영구적으로 지우겠습니까?" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Weet je zeker dat je project \"%@\" en alle bestanden daarin wilt verwijderen?" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tem certeza de que pretende remover o projeto \"%@\" e todos os arquivos contidos nele?" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tem a certeza que pretende eliminar o projeto \"%@\" e todos os seus ficheiros?" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вы уверены, что хотите удалить проект \"%@\" и все файлы в нем?" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "\"@\" projesini ve içindeki tüm dosyaları kaldırmak istediğinizden emin misiniz?" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вы впевнені що хочете видалити директорію \"%@\"?" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "您确定要删除项目\"%@\"和它下面的所有文件吗?" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "您確定要刪除項目\"%@\"和它下面的所有檔案嗎?" } } } }, "Back up storage" : { "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تخزين النسخ الاحتياطي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zálohovat úložiště" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Speicher sichern" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Almacenamiento de respaldo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sauvegarder le stockage" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גבה אחסון" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संग्रहण का बैकअप लें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Salva archivio" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "バックアップストレージ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "저장소 백업" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Back-up opslag" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Armazenamento de cópia de segurança" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Cópia de segurança de armazenamento" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать резервную копию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Depolamayı yedekle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зробити резервну копію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "提交git的存储库中的所有文件" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "將所有檔案提交到 Git 儲存庫" } } } }, "Cancel" : { "comment" : "Delete menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الغاء" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zrušit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Abbrechen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cancelar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Annuler" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ביטול" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "रद्द करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Annulla" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "キャンセル" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "취소" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Annuleer" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Cancelar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Cancelar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Отмена" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İptal" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відміна" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "取消" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "取消" } } } }, "Change creation date" : { "comment" : "Menu", "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تغيير تاريخ الإنشاء" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Změnit datum vytvoření" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erstellungsdatum ändern" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cambiar la fecha de creación" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Modifier la date de création" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה את תאריך היצירה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "निर्माण तारीख बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Modifica la data di creazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "作成日の変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "생성 날짜 변경" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Aanmaakdatum wijzigen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Alterar data de criação" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Alterar data de criação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Изменить дату создания" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Oluşturma tarihini değiştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Змінити дату створення" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更改创建日期" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "變更建立日期" } } } }, "Change Creation Date" : { "comment" : "File Menu\nMenu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تغيير تاريخ الإنشاء" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Změnit datum vytvoření" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erstellungsdatum ändern" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cambiar fecha de creación" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Modifier la date de création" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה תאריך יצירה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "निर्माण तारीख बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Modifica la data di creazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "作成日を変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "생성 일 변경" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wijzig de aanmaakdatum" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Alterar data de criação" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Alterar data de criação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Изменить дату создания" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Oluşturma Tarihini Değiştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Змінити дату створення" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "变更建立日期" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "變更建立日期" } } } }, "Clear" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Clear" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vyprázdnit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Klar" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Limpiar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Effacer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נקה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "साफ करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Pulisci" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "クリア" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Clear" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Clear" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Limpar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Limpar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Очистить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Temizle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Очистити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "清除" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "清除" } } } }, "Clipboard successfully saved" : { "localizations" : { "es" : { "stringUnit" : { "state" : "translated", "value" : "Portapapeles guardado correctamente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Буфер обмена успешно сохранен" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Pano başarıyla kaydedildi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Буфер обміну успішно збережено" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "剪貼簿已成功儲存" } } } }, "Close" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اغلاق" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zavřít" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Schließen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cerrar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Fermer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "סגור" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बंद करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Chiudi" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "閉じる" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "닫기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Sluit" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Fechar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Fechar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Закрыть" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kapat" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Закрити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "关闭" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "關閉" } } } }, "Commit & Push “%@”" : { "comment" : "Menu Library", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Закоммитить “%@”" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Закомітити “%@”" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push “%@”" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "送出並上傳“%@”" } } } }, "Commit message:" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Commit message:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Popisek commitu:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Commit message:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Commit message:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Message du Commit :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אשר הודעה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कमिट संदेश:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Commit message:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Commit message:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Commit message:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Commit message:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mensagem de confirmação:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Commit message:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сообщение коммита:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Mesajı kaydet:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Коментар до коміту:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "提交消息:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "送出訊息" } } } }, "Connection established successfully 🤟" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تم الاتصال بنجاح 🤟" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Připojení úspěšně navázáno 🤟" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Verbindung erfolgreich hergestellt 🤟" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Connection established successfully 🤟" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Connexion établie avec succès 🤟" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "החיבור נוצר בהצלחה 🤟" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कनेक्शन सफलतापूर्वक स्थापित हो गया 🤟" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Connessione stabilita con successo 🤟" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "接続が確立されました🤟" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "성공적으로 연결되었습니다 🤟" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Connection established successfully 🤟" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Conexão estabelecida com sucesso 🤟" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Conexão estabelecida com sucesso 🤟" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Соединение успешно установлено 🤟" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bağlantı başarıyla kuruldu 🤟" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "З'єднання встановлено успішно 🤟" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "连接建立成功🤟" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "連線已成功建立🤟" } } } }, "Convert to Plain" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Convert to Plain" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Převést na prostý text" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Konvertieren zu Plain" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Convertir a Plain" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Convertir en texte brut" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "המר לטקסט רגיל" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सादे में परिवर्तित करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Convertire in PlainText" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "に変換 PlainText" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "로 변환하다 Plain" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Omzetten naar Plain" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Converter para texto simples" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Converter para Plain" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Преобразовать в Plain" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Düze Dönüştür" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перетворити в PlainText" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "转换成纯文本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "轉換為純文字" } } } }, "Convert to TextBundle" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Convert to TextBundle" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Převést na TextBundle" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Konvertieren zu TextBundle" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Convertir a TextBundle" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Convertir en TextBundle" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "להמיר ל TextBundle" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टेक्स्टबंडल में परिवर्तित करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Convertire in TextBundle" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "に変換 TextBundle" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "로 변환하다 TextBundle" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Omzetten naar TextBundle" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Converter para TextBundle" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Converter para TextBundle" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Преобразовать в TextBundle" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle'a Dönüştür" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перетворити в TextBundle" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "转换成 TextBundle" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "轉換為TextBundle" } } } }, "Copy HTML" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نسخ HTML" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kopírovat HTML" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "HTML kopieren" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copiar HTML" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copier HTML" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העתק HTML" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "HTML कॉपी करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copia HTML" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "HTMLをコピー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "HTML 복사하기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kopieer HTML" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Copiar HTML" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Copiar HTML" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Скопировать HTML" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "HTML'yi Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скопіювати HTML" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "拷贝HTML" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "複製HTML" } } } }, "Copy Link" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نسخ الرابط" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zkopírovat odkaz" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Link kopieren" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copiar enlace" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copier le lien" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העתק קישור" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लिंक कॉपी करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copia Link" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リンクをコピー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "링크 복사하기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kopieer Link" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Copiar Link" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Copiar Ligação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Скопировать ссылку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bağlantıyı Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скопіювати посилання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "拷贝链接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "複製連結" } } } }, "Copy Plain Text" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نسخ نص اعتيادي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kopírovat prostý text" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Plain Text kopieren" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copiar texto sin estilo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copier le texte brut" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העתק מלל פשוט" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सादा पाठ कॉपी करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copia Testo Semplice" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "プレインテキストとしてコピー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "일반 텍스트 복사하기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kopieer Platte Tekst" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Copiar Texto Simples" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Copiar Texto Simples" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Скопировать чистый текст" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Düz Metni Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скопіювати текст" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "拷贝纯文本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "複製純文字" } } } }, "Copy Title" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نسخ العنوان" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kopírovat nadpis" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Title kopieren" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copiar título" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copier le titre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העתק כותרת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक कॉपी करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copia titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タイトルをコピー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "제목 복사" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kopieer titel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Copiar título" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Copiar título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Скопировать заголовок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlığı Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скопіювати заголовок" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "拷贝标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "複製標題" } } } }, "Copy URL" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نسخ URL" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kopírovat URL" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "URL kopieren" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copiar enlace" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copier l'URL" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העתק כתובת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "यूआरएल कॉपी करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copia URL" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "URLをコピー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "URL 복사" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kopieer URL" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Copiar URL" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Copiar endereço URL" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Скопировать ссылку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "URLyi Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скопіювати посилання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "拷贝URL" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "複製 URL" } } } }, "Create Folder" : { "comment" : "Menu Library", "localizations" : { "es" : { "stringUnit" : { "state" : "translated", "value" : "Crear carpeta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать директорию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasör Oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Створити директорію" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "建立資料夾" } } } }, "Create Web Page" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إنشاء صفحة ويب" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vytvořit webovou stránku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Online teilen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Crear página web" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Créer une page Web" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "צור דף אינטרנט" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब पेज बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Crea pagina web" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ウェブページの作成" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "웹 페이지 만들기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Create Web Page" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Criar página Web" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Criar página da Web" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать веб-страницу" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web Sayfası Oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Створити веб-сторінку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "创建为网页URL" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "建立網頁" } } } }, "Creation date:" : { "comment" : "Menu", "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تاريخ الإنشاء:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Datum vytvoření:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erstellungsdatum:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Fecha de creación:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Date de création :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תאריך יצירה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "निर्माण तारीख:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Data di creazione:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "作成日:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "제작 일:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Creation date:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Data de criação:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Data de criação:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Дата создания:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Oluşturma tarihi:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Дата створення:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "创建日期:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "建立日期:" } } } }, "Current password does not match with password in keychain" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "كلمة المرور الحالية لا تتطابق مع كلمة المرور في keychain" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Současné heslo se neshoduje s heslem v klíčence" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Das aktuelle Passwort stimmt nicht mit dem Passwort im Schlüsselbund überein" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "La contraseña actual no coincide con la contraseña en el Llavero" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Le mot de passe actuel ne correspond pas au mot de passe du trousseau" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסיסמה הנוכחית לא תואמת עם הסיסמה בצרור" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वर्तमान पासवर्ड कीचेन में दिए गए पासवर्ड से मेल नहीं खाता" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "La password corrente non corrisponde alla password nel portachiavi" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "現在のパスワードはKeychainに保存されたものと一致しません" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "암호가 키체인과 다릅니다" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Het huidige wachtwoord komt niet overeen met het wachtwoord in de sleutelhanger" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "A senha atual não corresponde à senha no keychain" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "A palavra-passe atual não coincide com a palavra-passe armazenada no keychain" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пароль не соответствует паролю в связке" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Geçerli parola anahtarlık içindeki parolayla uyuşmuyor" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Пароль не співпадає з паролем у зв'язці" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "当前密码与钥匙串中的密码不匹配" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "目前密碼與鑰匙圈中的密碼不符" } } } }, "Decrypt" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فك تشفير" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Dešifrovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Entschlüsseln" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Descifrar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déchiffrer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פענח" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डिक्रिप्ट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Decifra" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "復号化" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "복호화" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Decrypt" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Descriptografar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Descriptografar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Расшифровать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Şifresini Çöz" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розшифрувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "解密" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "解密" } } } }, "Decrypt Folder" : { "comment" : "Menu Library", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فك تشفير المجلد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Dešifrovat složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner verschlüsseln" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Descifrar carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Crypter le dossier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצפנת תיקיה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ोल्डर एन्क्रिप्ट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Crittografa cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォルダ復号" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "폴더 암호화" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Map ontsleutelen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Criptografar pasta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Descriptografar pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Снять шифрование" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasörü Şifresini Çöz" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зняти шифрування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "解密文件夹" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "解密資料夾" } } } }, "Default storage path should not be equal to Git path." : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "يجب ألا يكون مسار التخزين الافتراضي مساويًا لمسار Git." } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Cesta pro výchozí úložiště by neměla být totožná s cestou ke Gitu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Der Standardspeicherpfad sollte nicht gleich dem Git-Pfad sein." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "La carpeta de almacenamiento por defecto no puede ser un repositorio Git." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Le chemin de stockage par défaut ne doit pas être égal au chemin Git." } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נתיב האחסון צריך לא להיות שווה לנתיב Git." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डिफ़ॉल्ट संग्रहण पथ गिट पथ के बराबर नहीं होना चाहिए।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Il percorso di archiviazione predefinito non deve essere uguale al percorso Git." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "デフォルトストレージパスはGitパスとは異なる必要があります" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "기본 저장 경로는 Git 경로와 같지 않아야 합니다." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Default storage path should not be equal to Git path." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "O caminho de armazenamento padão não deve ser igual ao caminho do Git." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "O caminho de armazenamento por defeito não deve ser igual ao ao path de Git." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Путь к хранилищу по умолчанию не должен совпадать с путем Git." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Varsayılan depolama yolu\nGit yoluna eşit olmamalıdır." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Шлях зберігання за умовчанням не повинен дорівнювати шляху Git." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "默认存储路径不允许和 Git 路径相同。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "預設儲存路徑不應與 Git 路徑相同。" } } } }, "Delete" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حذف" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Smazat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Löschen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מחק" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Elimina" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "삭제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verwijder" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Deletar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удаление" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sil" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "刪除" } } } }, "Delete Folder" : { "comment" : "Menu Library", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حذف المجلد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Smazat složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner löschen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer le dossier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מחק תיקיה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ोल्डर हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Elimina cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォルダを削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "폴더 삭제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verwijder map" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Deletar Pasta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить директорию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasörü Sil" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити директорію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除文件夹" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "刪除資料夾" } } } }, "Delete Tag" : { "comment" : "Menu Library", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حذف العلامة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Smazat značku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Tag löschen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar etiqueta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer l’étiquette" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מחק תג" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टैग हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Elimina tag" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タグを削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "태그 삭제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verwijder tag" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Deletar Tag" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar Etiqueta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить тег" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etiketi Sil" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити тег" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除标签" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "刪除標籤" } } } }, "Delete Web Page" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حذف صفحة الويب" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Smazat webovou stránku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Geteilte löschen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar página web" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer la page Web" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מחק דף אינטרנט" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब पेज हटाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Elimina pagina web" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ウェブページを削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "웹 페이지 삭제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Webpagina verwijderen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Deletar página Web" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Excluir página da Web" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить веб-страницу" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web Sayfasını Sil" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити веб-сторінку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除网页" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "刪除網頁" } } } }, "Do you want force checkout?" : { "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Do you want force checkout?" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Chcete vynutit checkout?" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Do you want force checkout?" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Do you want force checkout?" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Voulez-vous forcer le checkout ?" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "האם ברצונך לבצע צ'ק-אאוט בכוח?" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्या आप जबरन चेकआउट चाहते हैं?" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Do you want force checkout?" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Do you want force checkout?" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Do you want force checkout?" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Do you want force checkout?" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Deseja forçar a saída?" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Do you want force checkout?" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сделать принудительный чекаут?" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ödemeyi zorunlu kılmak ister misiniz?" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зробити примусовий чекаут?" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "是否要强制检查?" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "您確定要強制切換嗎?" } } } }, "Do you want to move current notes in the new destination?" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "هل تريد نقل الملاحظات الحالية إلى الوجهة الجديدة؟" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Chcete přesunout současné poznámky do nového cíle?" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Möchten Sie aktuelle Notizen an das neue Ziel verschieben?" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "¿Quieres mover las notas al nuevo destino?" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Voulez-vous déplacer les notes actuelles vers la nouvelle destination ?" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "האם ברצונך להעביר את הפתקים הנוכחים ליעד החדש?" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्या आप वर्तमान नोट्स को नए गंतव्य पर ले जाना चाहते हैं?" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Vuoi spostare le note correnti nella nuova destinazione?" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "既存のノートを新しい場所に移動しますか?" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "현재 메모를 새 대상으로 이동하시겠습니까?" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Do you want to move current notes in the new destination?" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pretende mover as notas atuais para o novo destino?" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Deseja mover as notas existentes para o novo destino?" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вы хотите переместить текущие заметки в новое место назначения?" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Mevcut notları yeni hedefe taşımak ister misiniz?" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ви хочете перемістити поточні нотатки до нового сховища?" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "您想将当前笔记移动到目标处吗?" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "您要將目前的筆記移動到新目的地嗎?" } } } }, "Documents" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "المستندات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Dokumenty" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Documents" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Documentos" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Documents" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מסמכים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "दस्तावेज़" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Documenti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ドキュメント" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "서류" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Documents" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Documentos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Documentos" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Документы" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Belgeler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Документація" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "文档" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "文件" } } } }, "Done" : { "localizations" : { "es" : { "stringUnit" : { "state" : "translated", "value" : "Hecho" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Готово" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tamam" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Готово" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "完成" } } } }, "Duplicate" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نسخة مكررة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Duplikovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Duplizieren" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Duplicar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dupliquer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שכפל" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नकल" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Duplica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "複製" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "복제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Dupliceer" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Duplicar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Duplicar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать копию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Дублювати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "生成副本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "製作複本" } } } }, "Edit Link…" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تحرير الرابط ..." } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Upravit odkaz…" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Edit Link…" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Editar enlace…" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Modifier le lien…" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ערוך קישור..." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लिंक संपादित करें…" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Modifica Link…" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リンクを修正..." } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "링크 수정…" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Bewerk Link..." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Editar Link..." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Editar Ligação..." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Редактировать ссылку..." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bağlantıyı Düzenle…" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Редагувати посилання..." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "编辑链接…" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "編輯連結…" } } } }, "Empty Bin" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إفراغ سلة المهملات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vyprázdnit koš" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Papierkorb leeren" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Vaciar papelera" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Vider la corbeille" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "רוקן סל" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खाली बिन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Svuota cestino" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "空のゴミ箱" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "휴지통 비우기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Prullenbak leegmaken" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Esvaziar lixeira" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Esvaziar caixa de lixo" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Очистить корзину" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Çöp Kutusunu Boşalt" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Очистити кошик" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "清空回收站" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "清空垃圾桶" } } } }, "Empty password" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "كلمة مرور فارغة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Prázdné heslo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passwort leer" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Contraseña vacía" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mot de passe vide" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסיסמה ריקה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "ख़ाली पासवर्ड" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Password vuota" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスワードが空です" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "빈 암호" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Leeg wachtwoord" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Senha vazia" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Palavra-passe vazia" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Не введен пароль" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Boş şifre" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Введіть пароль" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "空密码" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "密碼為空" } } } }, "Enter an encryption password:" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "أدخل كلمة مرور تشفير:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vytvořte šifrovací heslo:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Geben Sie ein Verschlüsselungspasswort ein:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Introduzca una contraseña de cifrado:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Saisir un mot de passe de chiffrement :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הזן סיסמת הצפנה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "एन्क्रिप्शन पासवर्ड दर्ज करें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Inserire una password di crittografia:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "暗号化パスワードを入力:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "암호화 비밀번호를 입력하세요:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voer een coderingswachtwoord in:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Introduzir uma senha de encriptação:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Introduzir uma palavra-passe de encriptação:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Введите пароль шифрования:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bir şifreleme parolası girin:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Введіть пароль шифрування:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "输入加密密码:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請輸入加密密碼:" } } } }, "Enter Master Password" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ادخل كلمة المرور الاساسية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zadat hlavní heslo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Geben Sie das Master Passwort ein" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Introduce la contraseña maestra" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Entrez le mot de passe principal" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הכנס סיסמה ראשית" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मास्टर पासवर्ड दर्ज करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Inserisci la Master Password" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "マスターパスワードを入力する" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "마스터 암호 입력" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voer Hoofdwachtwoord in" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Introduza a senha mestre" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Introduzir Palavra-passe Mestra" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Введите мастер пароль" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ana Parolayı Girin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Введіть мастер пароль" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "输入管理员密码" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請輸入主密碼" } } } }, "Folder with name '%@' already exist" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "المجلد الذي يحمل الاسم '٪ @' موجود بالفعل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Složka s názvem „%@“ už existuje" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Folder with name '%@' already exist" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ya existe una carpeta nombrada '%@'" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Le dossier avec le nom '%@' existe déjà" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תיקיה בשם \"%@\" כבר קיימת." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "'%@' नाम वाला फ़ोल्डर पहले से मौजूद है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "La cartella con il nome '%@' già esiste" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "'%@'という名前のフォルダはすでに存在しています" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이름이 '%@'인 폴더가 이미 있습니다." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Folder with name '%@' already exist" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "A pasta com o nome '%@' já existe" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Já existe uma pasta com o nome '%@'" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Директория с названием '%@' уже существует" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "'%@' adlı klasör zaten mevcut" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Директорія з назвою '%@' вже існує" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "文件夹名称 '%@' 已存在" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "名為'%@' 的資料夾已存在" } } } }, "Font" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Font" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Písmo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Font" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Font" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Police" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גופן" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ॉन्ट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Font" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Font" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Font" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Font" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Fonte" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Font" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Шрифт" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Font" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Шрифт" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "字体" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "字體" } } } }, "Force Delete" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حذف اجباري" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vynutit smazání" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Löschen erzwingen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Forzar la Eliminación" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Forcer la suppression" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מחק מיידית" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बलपूर्वक हटाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Forza Cancellazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "強制的に削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "강제 삭제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Forceer verwijderen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Forçar deletar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Forçar Apagar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Принудительное удаление" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Zorla Sil" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Примусово видалити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "强制删除" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "強制刪除" } } } }, "FSNotes [edit]" : { "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [تعديل]" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [upravit]" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [bearbeiten]" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [edición]" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [modification]" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [עריכה]" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [संपादन]" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [modifica]" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [編集]" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [수정]" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [bewerk]" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [editar]" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [editar]" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [редактирование]" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [düzenle]" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [редагування]" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [编辑]" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes[編輯]" } } } }, "FSNotes [preview]" : { "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [المعاينة]" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [náhled]" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [Vorschau]" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [previsualización]" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [prévisualisation]" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [תצוגה]" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [पूर्वावलोकन]" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [anteprima]" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [プレビュー]" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [미리보기]" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [voorvertoning]" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [pré-visualização]" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [pré-visualizar]" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [предпросмотр]" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [önizleme]" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [попередній перегляд]" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes [预览]" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes[預覽]" } } } }, "git error" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "git error" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "git chyba" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "git error" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "git error" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "erreur git" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "git error" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गिट त्रुटि" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "git error" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "git error" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "git error" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "git error" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "git error" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "git error" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ошибка git" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "git hatası" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "git error" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "git error" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "git 錯誤" } } } }, "Git error" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Git error" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Git chyba" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Git error" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Git error" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Erreur Git" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Git error" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गिट त्रुटि" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Git error" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Git error" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Git error" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Git error" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Git error" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Git error" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ошибка git" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Git hatası" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Git error" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Git 错误" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "git 錯誤" } } } }, "Git push error" : { "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Git push خطأ" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Git push chyba" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Git push fehler" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Git push error" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Erreur Git push" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Git push שְׁגִיאָה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गिट पुश त्रुटि" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Git push errore" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Git push エラー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Git push 오류" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Git push error" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Git push error" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Git push erro" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ошибка git push" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Git Gönderme hatası" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Git push помилка" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Git 推送错误" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Git 推送錯誤" } } } }, "Git repository already exists, delete it and clone again??" : { "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Git repository already exists, delete it and clone again??" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Git repozitář už existuje, chcete ho smazat a klonovat znovu?" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Git repository already exists, delete it and clone again??" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Git repository already exists, delete it and clone again??" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Le dépôt Git existe déjà, voulez-vous le supprimer et le cloner à nouveau ??" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Git repository already exists, delete it and clone again??" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गिट रिपोजिटरी पहले से मौजूद है, इसे हटाएँ और फिर से क्लोन करें??" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Git repository already exists, delete it and clone again??" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Git repository already exists, delete it and clone again??" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Git repository already exists, delete it and clone again??" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Git repository already exists, delete it and clone again??" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "O repositório Git já existe, apagar-lo e clona-lo novamente??" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Git repository already exists, delete it and clone again??" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Git-репозиторий уже существует, удалить его и клонировать заново?" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Git deposu zaten var, silinip tekrar klonlansın mı?" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Git repository already exists, delete it and clone again??" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Git 存储库已存在,请将其删除并再次克隆??" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Git 儲存庫已存在,是否刪除並重新複製?" } } } }, "Hide Note List" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إخفاء قائمة الملاحظات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Skrýt seznam poznámek" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notizliste ausblenden" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar notas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Masquer la liste des notes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסתר רשימת הערות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट सूची छिपाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nascondi l'elenco delle note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ノート リストを非表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "메모 목록 숨기기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verbergen notitielijst" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar lista de notas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar lista de notas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Спрятать список заметок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Not Listesini Gizle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Приховати нотатки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "隐藏笔记列表" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "隱藏筆記列表" } } } }, "Hide Sidebar" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اخفاء الشريط الجانبي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Skrýt boční panel" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Seitenleiste ausblenden" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar barra lateral" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Masquer la barre latérale" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסתר סרגל צד" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "साइडबार छिपाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nascondi sidebar" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サイドバーを隠す" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "사이드바 숨기기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verbergen zijbalk" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar menu lateral" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar barra lateral" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Спрятать сайдбар" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kenar Çubuğunu Gizle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Приховати сайдбар" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "隐藏边栏" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "隱藏側邊欄" } } } }, "History" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تاريخ" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Historie" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Die geschichte" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Historia" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Historique" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "היסטוריה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इतिहास" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cronologia" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "履歴" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "변경 이력" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Geschiedenis" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Histórico" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Histórico" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "История" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Geçmiş" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Історія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "笔记历史版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "歷史記錄" } } } }, "iCloud Drive" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Drive iCloud" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "iCloud 云盘" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "iCloud 雲碟" } } } }, "Import" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "استيراد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Importovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Importieren" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Importar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Importer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ייבא" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "आयात" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Importa" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "取り込む..." } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "가져오기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Importeren" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Importar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Importar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Импорт" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İçe aktar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Імпортувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "导入" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "匯入" } } } }, "Inbox" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الوارد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Příchozí" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Posteingang" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Entrada" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Boîte de réception" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תיבת דואר נכנס" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इनबॉक्स" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "In Entrata" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "未整理" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Inbox" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Postvak In" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Inbox" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Caixa de entrada" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Входящие" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Gelen kutusu" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вхідні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "收集箱" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "收件匣 " } } } }, "Libgit2 error" : { "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 error" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Chyba libgit2" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 error" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 error" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Erreur libgit2" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 error" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 त्रुटि" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 error" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 error" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 error" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 error" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 error" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 error" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ошибка libgit2" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 hatası" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 error" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 错误" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 錯誤" } } } }, "Libgit2 error: (code)" : { "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 خطأ: (code)" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Chyba libgit2: (code)" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 fehler: (code)" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 error: (code)" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Erreur Libgit2 : (code)" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 שְׁגִיאָה: (code)" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 त्रुटि: (कोड)" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 errore: (code)" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 エラー: (code)" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 오류: (code)" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 error: (code)" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 error: (code)" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 erro: (code)" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ошибка libgit2: (код)" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 hatası : (code)" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 помилка: (code)" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 错误: (code)" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Libgit2 錯誤:(代碼)" } } } }, "Link" : { "extractionState" : "migrated", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "رابط" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odkaz" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Link" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Enlace" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Lien" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "קישור" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लिंक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Link" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リンク" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "링크" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Link" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Link" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ligação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ссылка" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bağlantı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Посилання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "链接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "連結" } } } }, "Lock" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "قفل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zamknout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sperren" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cerrar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Verrouiller" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נעל" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लॉक करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Serratura" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ロック" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "자물쇠" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Lock" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Trancar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заблокировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kilit" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заблокувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "上锁" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "鎖定" } } } }, "Lock All Encrypted" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "قفل جميع المشفرة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zamknout všechny zašifrované" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Alle verschlüsselte sperren" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Bloquar todas las encriptadas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Verrouiller les documents chiffrés" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נעל פתקים מוצפנים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सभी एन्क्रिप्टेड लॉक करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Blocca tutte le note criptate" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ロックされたノートをすべて閉じる" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "모든 암호화 된 노트 잠금" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Vergrendel alle versleutelde" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear todos os criptografados" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear Todos os Criptografados" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заблокировать все секретные" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tüm Şifrelenmişleri Kilitle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заблокувати всі секретні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "锁定所有加密的笔记" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "鎖定所有已加密項目" } } } }, "Lock Folder" : { "comment" : "Menu Library", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "قفل المجلد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zamknout složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner sperren" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Verrouiller le dossier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נעל תיקייה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ोल्डर लॉक करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Blocca cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォルダロック" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "자물쇠" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Map vergrendelen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear pasta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заблокировать директорию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasörü Kilitle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заблокувати директорію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "锁定文件夹" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "鎖定資料夾" } } } }, "Locked" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Locked" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zamčeno" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Abgeschlossen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Bloqueado" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Verrouillé" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Locked" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लॉक की गई" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Bloccato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ロック" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "잠김" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Gesloten" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Bloqueado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Bloqueado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заблокированный" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kilitli" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Замкнено" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "锁定" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "鎖定" } } } }, "Make Link" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Make Link" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vytvořit odkaz" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Make Link" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Make Link" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Créer un Lien" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Make Link" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लिंक बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Make Link" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Make Link" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Make Link" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Make Link" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Criar Link" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Make Link" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сделать ссылку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bağlantı Yap" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Make Link" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "建立链接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "建立連結" } } } }, "Master password:" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "كلمة المرور الاساسية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hlavní heslo:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Master Passwort:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Contraseña maestra:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mot de passe maître :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "סיסמה ראשית:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मास्टर पासवर्ड:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Master password:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "マスターパスワード:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "마스터 암호:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hoofdwachtwoord:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Senha mestre:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Palavra-passe mestra:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Мастер пароль:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ana şifre:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Мастер пароль" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "管理员密码:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "主密碼:" } } } }, "Move" : { "comment" : "File Menu\nMenu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نقل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přesunout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Verschieben" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mover" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déplacer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העבר" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्थानांतरण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sposta" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "移動" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이동" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verplaats" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mover" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mover" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переместить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Taşı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перемістити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "移动" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "移動" } } } }, "Move error" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نقل خطأ" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Chyba při přesouvání" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Fehler beim Verschieben" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Error de movimiento" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Erreur de déplacement" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שגיאת העברה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्थानांतरण त्रुटि" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Errore di spostamento" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "移動エラー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이동 오류" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Fout bij verplaatsen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Erro ao mover" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Erro de deslocação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ошибка перемещения" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Taşıma hatası" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Помилка переміщення" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "移动错误" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "移動錯誤" } } } }, "New" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "جديد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nový" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neu" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nuevo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nouvelle" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חדש" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नया" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuovo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新規" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새로운 노트" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Nieuw" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Novo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Novo" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нова нотатка" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "新建" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "新增" } } } }, "New folder" : { "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مجلد جديد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nová složka" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neuer Ordner" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nueva carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nouveau dossier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תיקיה חדשה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नया फ़ोल्डर" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuova cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新規フォルダ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새로운 폴더" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Nieuwe map" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nova pasta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nova pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать директорию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni Dosya" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нова директорія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "新建文件夹" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "新增資料夾" } } } }, "New Folder" : { "comment" : "Menu Library", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إنشاء مجلد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vytvořit složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neuer Ordner" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Crear carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nouveau dossier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תיקיה חדשה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फोल्डर बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuova cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォルダ作成" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새로운 폴더" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Map aanmaken" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Criar pasta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Criar pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать директорию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasör Oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нова директорія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "创建文件夹" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "新增資料夾" } } } }, "New Note" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ملاحظة جديدة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nová poznámka" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neue Notiz" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nueva nota" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nouvelle note" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הערה חדשה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नया नोट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuova nota" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新規ノート" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새 메모" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Nieuwe notitie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nova nota" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nova nota" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Новая заметка" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni Not" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нова нотатка" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "新建笔记" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "新增筆記" } } } }, "New Note in New Window" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ملاحظة جديدة في نافذة جديدة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nová poznámka v novém okně" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neue Notiz in neuem Fenster" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nueva nota en una ventana nueva" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nouvelle note dans une nouvelle fenêtre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הערה חדשה בחלון חדש" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नई विंडो में नया नोट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuova nota in una nuova finestra" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新規ノートを新規ウィンドウで開く" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새 창에서 새 메모 열기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Nieuwe notitie in nieuw venster" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nova nota em nova janela" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nova nota numa nova janela" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Новая заметка в новом окне" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni Pencerede Yeni Not" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нова нотатка у новому вікні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在新窗口中新建笔记" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在新視窗中新增筆記" } } } }, "New project" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مشروع جديد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nový projekt" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neues Projekt" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nuevo proyecto" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nouveau projet" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פרויקט חדש" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नया प्रोजैक्ट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuovo progetto" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新規プロジェクト" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새로운 프로젝트" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Nieuw project" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Novo projeto" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Novo projeto" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Новая директория" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni Proje" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нова директорія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "新建项目" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "新增專案" } } } }, "No" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "No" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Ne" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "No" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Non" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "לא" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नहीं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "No" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "いいえ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "아니" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "No" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Não" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Não" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Нет" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Hayır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "否" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "否" } } } }, "None Selected" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "None Selected" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nic nevybráno" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Keine ausgewählt" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ninguno Seleccionado" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aucune sélection" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "None Selected" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोई भी नहीं चुना गया" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nessuno selezionato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "選択なし" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "선택되지 않음" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Geen geselecteerd" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nada selecionado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nenhum selecionado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Не выбрано" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seçilmedi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не вибрано" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无选择" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "未選取任何項目" } } } }, "Note with name \"%@\" already exists in selected directory." : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ملاحظة بالاسم \"%@\" موجودة بالفعل في المجلد المحدد." } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Poznámka s názvem „%@“ už ve vybrané složce existuje." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Note with name \"%@\" already exists in selected directory." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ya existe una carpeta nombrada '%@' en el directorio seleccionado." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "La note avec le nom \"%@\" existe déjà dans le répertoire sélectionné." } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פתק בשם \"%@\" כבר קיים בתיקיה הנבחרת." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "चयनित निर्देशिका में \"%@\" नाम वाला नोट पहले से मौजूद है।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "La nota con il nome \"%@\" già esiste nella cartella selazionata." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ノート \"%@\" は指定したフォルダにすでに存在しています" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이름이 있는 메모 \"%@\" 선택한 디렉토리에 이미 존재합니다." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Opmerking met de naam \"%@\" bestaat al in de geselecteerde map." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nota com o nome \"%@\" já existe na pasta selecionada." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Já existe uma nota com o nome \"%@\" no directório selecionado." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заметка с названием \\\"%@\\\" уже существует в выбранной директории." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seçilen dizinde \"%@\" adlı not zaten mevcut." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нотатка з назвою \"%@\" вже існує у вибраной директорії." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "名称为 \"%@\" 的笔记已存在于所选目录中。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "所選目錄中已存在名為\"%@\" 的筆記。" } } } }, "Notes" : { "comment" : "Sidebar label", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ملاحظات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Poznámky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notizen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Notas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Notes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פתקים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "すべて" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "모든 노트" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Notities" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Notas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Notas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заметки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notlar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нотатки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "所有笔记" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "筆記" } } } }, "OK" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "موافق" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אישור" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "ठीक है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "확인" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tamam" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "好的" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "確定" } } } }, "Open External" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فتح خارجي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Otevřít externí" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In externem Editor öffnen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Abrir externo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir en externe" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פתח חיצוני" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बाहरी खोलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apri esternamente" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "開く…" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "외부에서 열기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Open extern" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Abrir External" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Abrir externamente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Внешний редактор" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Açık Harici" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відкрити зовні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "打开外部" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "開啟外部連結" } } } }, "Open note" : { "comment" : "Document opened", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "افتح الملاحظة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Otevřená poznámka" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notiz öffnen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Abrir nota" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Note ouverte" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פתח פתק" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट खोलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apri nota" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "開かれたノート" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "메모 열기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Notitie openen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Abrir nota" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Abrir nota" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открыть заметку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notu Aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відкрити нотатку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "打开笔记" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "開啟筆記" } } } }, "Open Note in New Window" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فتح الملاحظة في نافذة جديدة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Otevřít poznámku v novém okně" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notiz in neuem Fenster öffnen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Abrir nota en una ventana nueva" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir la note dans une nouvelle fenêtre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פתח הערה בחלון חדש" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नई विंडो में नोट खोलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apri nota in una nuova finestra" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ノートを新規ウィンドウで開く" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "메모 새 창에서 열기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Notitie openen in nieuw venster" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Abrir nota em nova janela" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Abrir nota numa nova janela" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открыть заметку в новом окне" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notu Yeni Pencerede Aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відкрити нотатку у новому вікні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在新窗口中打开笔记" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在新視窗中開啟筆記" } } } }, "Otherwise, the database of your notes will be available at: " : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "وإلا، ستكون قاعدة بيانات الملاحظات متوفرة على: " } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vaše databáze poznámek bude jinak k dispozici zde: " } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Otherwise, the database of your notes will be available at: " } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "De lo contrario la base de datos de tus notas estará disponible en: " } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sinon, la base de données de vos notes sera disponible sur : " } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אחרת, אחסון פתקיך יהיה נגיש ב:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अन्यथा, आपके नोट्स का डेटाबेस यहां उपलब्ध होगा:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Altrimenti, il database delle tue note sarà disponibile su: " } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "あるいは、データベースは次の場所で利用できます: " } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "그렇지 않으면 메모 데이터베이스를 다음에서 사용할 수 있습니다." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Otherwise, the database of your notes will be available at: " } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Caso contrário, a base de dados das suas notas estará disponível em: " } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "De outra forma, os dados das suas notas estarão disponíveis em:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "В противном случае база данных заметок будет доступна по адресу: " } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Aksi takdirde notlarınızın veritabanına şu adresten ulaşabilirsiniz:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Інакше база даних ваших нотаток буде доступна за адресою: " } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "否则,您的笔记数据库将在以下位置可用:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "否則,您的筆記資料庫將位於:" } } } }, "Password:" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "كلمة المرور:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Heslo:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passwort:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Contraseña:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mot de passe :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "סיסמה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासवर्ड" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Password:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスワード:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "비밀번호:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wachtwoord:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Senha:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Palavra-passe:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пароль:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Şifre:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Пароль:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "密码:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "密碼:" } } } }, "Path not available" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "المسار غير متوفر" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Cesta není k dispozici" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Path not available" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Carpeta no disponible" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Chemin non disponible" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הנתיב אינו זמין" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पथ उपलब्ध नहीं है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Percorso non disponibile" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "無効なパスです" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "경로를 사용할 수 없음" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Pad niet beschikbaar" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Caminho indisponível " } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Caminho não disponível" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Путь недоступен" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yol mevcut değil" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Шлях недоступний" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "路径不可用" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "路徑無法使用" } } } }, "Pin" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "دبوس" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Připnout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Fixieren" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Anclar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Épingler" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצמד" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पिन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Fissa" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "メモをピンで固定" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "고정" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Pin" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pin" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Fixar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Прикрепить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Pin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Закріпити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "置顶" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "釘選" } } } }, "Please enter image title:" : { "comment" : "Edit area", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الرجاء إدخال عنوان الصورة:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zadejte popis obrázku:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Geben Sie die Bildbezeichnung ein:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, introduce un título para la imagen:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Veuillez saisir le titre de l'image :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נא הכנס כותרת תמונה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कृपया छवि शीर्षक दर्ज करें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Inserisci il titolo dell'immagine:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "イメージタイトルを入力:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이미지 제목을 입력하십시오:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voer a.u.b. afbeeldingstitel in:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, insira um título na imagem:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Por favor insira o título da imagem:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пожалуйста, введите заголовок изображения:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Resim başlığını girin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок зображення:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "请输入图片标题:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請輸入圖片標題:" } } } }, "Please enter password for current note" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الرجاء إدخال كلمة المرور للملاحظة الحالية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zadejte heslo k této poznámce" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Geben Sie das Passwort für die aktuelle Notiz ein" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, introduce una contraseña para la nota" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Veuillez entrer le mot de passe pour la note actuelle" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נא הכבס סיסמה לפתק הנוכחי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कृपया वर्तमान नोट के लिए पासवर्ड दर्ज करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Inserisci la password per la nota corrente" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "現在開いているノートのためのパスワードを入力" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "노트 암호를 입력하십시오" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voer a.u.b. wachtwoord voor huidige notitie in:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, insira uma senha para nota atual" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Por favor insira a palavra-passe para a nota atual" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Введите пароль для текущей заметки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Lütfen güncel not için şifreyi giriniz" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Будь ласка, введіть пароль для нотатки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "请输入当前笔记的密码" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請輸入目前筆記的密碼" } } } }, "Please enter project name:" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الرجاء إدخال اسم المشروع:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zadejte název projektu:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Geben Sie den Projektname ein:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, introduce un nombre para el proyecto:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Veuillez saisir le nom du projet :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נא הכנב שם פרויקט:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कृपया प्रोजैक्ट का नाम दर्ज करें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Inserisci il nome del progetto:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "プロジェクト名を入力:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "프로젝트 이름을 입력하십시오:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voer a.u.b. projectnaam in:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, insira um nome no projeto:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Por favor insira o nome do projeto:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пожалуйста, введите название директории:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Lütfen proje adını giriniz:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Будь ласка, введіть назву директорії:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "请输入项目名称:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請輸入專案名稱:" } } } }, "Please enter tag name:" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الرجاء إدخال اسم الوسم:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zadejte jméno značky:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bitte geben Sie den Tag-Namen ein:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, introduce un nombre para la etiqueta:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Veuillez saisir le nom de l’étiquette :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נא הכנס שם תג:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कृपया टैग नाम दर्ज करें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Inserisci il nome del tag:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タグ名を入力:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "태그 이름을 입력하세요:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voer de tagnaam in:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, insira um nome na tag:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Por favor insira o nome da etiqueta:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Введите название тега:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Lütfen etiket adını girin:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Будь ласка, введіть назву тегу:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "请输入标签名称:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請輸入標籤名稱:" } } } }, "Please enter tags (comma separated):" : { "comment" : "Menu", "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "رجاءً ادخل وسوم (مفصولة بفارزة)" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zadejte značky (oddělené čárkami):" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Geben Sie Tags ein (durch Kommas getrennt):" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, introduce las etiquetas (separadas por comas):" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Veuillez saisir des étiquettes (séparées par des virgules) :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נא הכנס תגים (מופרדים בפסיק):" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कृपया टैग दर्ज करें (अल्पविराम से अलग करके):" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Inserisci i tag (separati da virgole):" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タグを入力(コンマ区切り):" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "태그를 입력하십시오(콤마로 구분):" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voer a.u.b. tags (kommagescheiden) in:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, insira tags (separadas por vírgulas):" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Por favor insira as etiquetas (separadas por vírgulas):" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пожалуйста введите теги (через запятую):" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Lütfen etiketleri girin (virgülle ayırarak):" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Будь ласка, введіть теги (розділити комою):" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "请输入标签(使用英文逗号分隔):" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請輸入標籤(以逗號分隔):" } } } }, "Please init git repository before (Preferences -> Git -> Init/commit)" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Please init git repository before (Preferences -> Git -> Init/commit)" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nejprve inicializujte repozitář (Nastavení > Git > Init/commit)" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Please init git repository before (Preferences -> Git -> Init/commit)" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Please init git repository before (Preferences -> Git -> Init/commit)" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "S’il vous plait, initialiser le dépôt git avant (Preferences -> Git -> Init/commit)" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Please init git repository before (Preferences -> Git -> Init/commit)" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कृपया पहले गिट रिपोजिटरी प्रारंभ करें (प्राथमिकताएं -> गिट -> आरंभ/कमिट)" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Please init git repository before (Preferences -> Git -> Init/commit)" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Please init git repository before (Preferences -> Git -> Init/commit)" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Please init git repository before (Preferences -> Git -> Init/commit)" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Please init git repository before (Preferences -> Git -> Init/commit)" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, inicie o repositório git antes (Preferências -> Git -> Init/commit)" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Please init git repository before (Preferences -> Git -> Init/commit)" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пожалуйста, предварительно инициируйте git-репозиторий (Preferences -> Git -> Init/commit)." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Lütfen git deposunu başlatmadan önce (Tercihler -> Git -> Başlat/gönder)" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Please init git repository before (Preferences -> Git -> Init/commit)" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Please init git repository before (Preferences -> Git -> Init/commit)" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請先初始化 Git 儲存庫 (偏好設定 -> Git -> 初始化/提交)" } } } }, "Please select private and public key" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الرجاء تحديد مفتاح خاص وعام" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vyberte prosím veřejný a soukromý klíč" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bitte wählen Sie privaten und öffentlichen Schlüssel aus" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Seleccione la clave pública y privada" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Veuillez sélectionner une clé privée et une clé publique" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אנא בחר מפתח פרטי וציבורי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कृपया निजी और सार्वजनिक कुंजी चुनें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Seleziona la chiave privata e pubblica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "秘密鍵と公開鍵を選択してください" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "개인 키와 공개 키를 선택하세요." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Selecteer a.u.b. privé en openbare sleutel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, selecione uma chave privada e pública" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, selecione a chave privada e pública" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пожалуйста, выберите закрытый и открытый ключ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Lütfen özel ve genel anahtarı seçin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Будь ласка, оберіть приватний та публічний ключ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "请选择私钥和公钥" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請選擇私鑰和公鑰" } } } }, "Please select private key" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الرجاء تحديد المفتاح الخاص" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vyberte prosím soukromý klíč" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bitte wählen Sie den privaten Schlüssel aus" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Por favor seleccione clave privada" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Veuillez sélectionner la clé privée" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אנא בחר מפתח פרטי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कृपया निजी कुंजी चुनें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Seleziona la chiave privata" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "秘密鍵を選択してください" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "개인 키를 선택하십시오" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Selecteer a.u.b. privésleutel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, insira uma chave privada" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Selecione a chave privada" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Выберите закрытый ключ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Lütfen özel anahtarı seçin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Будь ласка, виберіть приватний ключ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "请选择私钥" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請選擇私鑰" } } } }, "Please try again" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حاول مرة اخرى" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zkuste to znovu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Versuchen Sie bitte nochmal" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Por favor intenta de nuevo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Veuillez réessayer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נסה שוב" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कृपया पुन: प्रयास करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Per favore riprova" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "もう一度試してください" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다시 시도하십시오" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Probeer het a.u.b. opnieuw" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Por favor, tente novamente" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Volte a tentar por favor" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пожалуйста, попробуйте ещё раз" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Lütfen tekrar deneyin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Буль ласка, спробуйте знову" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "请再试一次" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請再試一次 " } } } }, "Preferences" : { "comment" : "Localizable.strings\n FSNotes\n \n Created by Oleksandr Glushchenko on 7/4/18.\n Copyright © 2018 Oleksandr Glushchenko. All rights reserved.", "extractionState" : "migrated", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "التفضيلات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Předvolby" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einstellungen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Preferencias" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Préférences" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העדפות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्राथमिकताएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Preferenze" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "設定" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "환경설정" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voorkeuren" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Preferências" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Preferências" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Настройки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tercihler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Налаштування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "偏好设置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "偏好設定" } } } }, "Print" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "طباعة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Tisknout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Drucken" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Imprimir" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Imprimer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הדפס" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रिंट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Stampa" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "印刷" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "프린트" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Druk af" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Imprimir" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Imprimir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Печать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yazdır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Роздрукувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "打印" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "列印" } } } }, "Quit FSNotes" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اغلاق FSNotes" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Ukončit FSNotes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes beenden" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Salir de FSNotes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Quitter FSNotes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "סיים את FSNotes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes बंद करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Esci da FSNotes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FSNotesを終了" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes 종료" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Stop FSNotes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Sair do FSNotes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Sair do FSNotes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Выйти из FSNotes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes'tan çıkın" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Завершити роботу FSNotes" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "退出FSNotes" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "結束 FSNotes" } } } }, "Recents" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الحديثة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nedávné" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Letzte" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Recientes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Récents" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אחרונים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हाल ही का" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Recenti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "最近" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "최근" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Recente" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Recentes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Recentes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Недавние" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Arananlar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Останні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "最近" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "最近項目" } } } }, "Recents Search" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "البحث الأخير" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat nedávné" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neueste Suche" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Búqueda de Recientes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Recherches récentes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חיפושים אחרונים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हाल की खोजें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ricerche Recenti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "最近の検索" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "최근 검색" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Recent zoeken" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Buscas recentes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Procurar Recentes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Недавние запросы" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Son Arananlar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Останні запити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "最近搜索" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "最近搜尋紀錄" } } } }, "Remove" : { "comment" : "Delete menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حذف" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odebrat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Löschen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Borrar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסר" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rimuovi" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "삭제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verwijder" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Remover" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Remover" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "移除" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "移除" } } } }, "Remove Link" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حذف الرابط" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odebrat odkaz" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Remove Link" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar enlace" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer le lien" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסר קישור" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लिंक हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rimuovi Link" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リンクを削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "링크 제거" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verwijder Link" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Remover Link" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Remover Ligação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить ссылку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bağlantıyı Kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити посилання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除链接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "移除連結" } } } }, "Remove note(s)" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حذف ملاحظة/ملاحظات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odebrat poznámku/y" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notiz (en) entfernen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar nota(s)" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer la(les) note(s)" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסר פתקים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट हटाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rimuovi nota/e" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ノートを削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "노트 삭제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verwijder notitie(s)" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Remover nota(s)" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar nota(s)" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить заметки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notu(ları) kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити нотатку(ок)" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "移除笔记" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "移除筆記" } } } }, "Remove Tags" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إزالة العلامات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odebrat značky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Tags entfernen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar etiquetas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer les étiquettes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסר תגים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टैग हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rimuovi tag" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タグを削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "태그 제거" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verwijderen tags" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Remover Tags" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Remover Etiquetas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить теги" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etiketleri Kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити теги" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除标签" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "移除標籤" } } } }, "Rename" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اعادة تسمية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přejmenovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Umbenennen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Renombrar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Renommer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה שם" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नाम बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "名前を変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이름 변경" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hernoem" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Renomear" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomear" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeniden isimlendir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейменувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重命名" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重新命名" } } } }, "Rename Folder" : { "comment" : "Menu Library", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اعادة تسمية المجلد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přejmenovat složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner umbenennen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Renombar carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Renommer le dossier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה שם תיקיה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ोल्डर का नाम बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォルダの名称変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "노트 이름 변경" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hernoem map" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Renomear pasta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomear pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать директорию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasörü Yeniden Adlandır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейменувати директорію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重命名文件夹" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重新命名資料夾" } } } }, "Rename Tag" : { "comment" : "Menu Library", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إعادة تسمية العلامة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přejmenovat značku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Tag umbenennen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Renombar etiqueta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Renommer l’étiquette" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה שם תג" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टैग का नाम बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina tag" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タグの名称変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "태그 이름 바꾸기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hernoemen tag" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Renomear Tag" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomear Etiqueta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать тег" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etiketi Yeniden Adlandır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейменувати тег" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重命名标签" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重新命名標籤" } } } }, "Rename Tags" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إعادة تسمية العلامات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přejmenovat značky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Tags umbenennen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Renombar etiqueta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Renommer les étiquettes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה שמות תגים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टैग का नाम बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina tag" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "複数タグの名称変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "태그 이름 바꾸기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hernoemen tags" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Renomear Tags" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomear Etiquetas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать теги" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etiketleri Yeniden Adlandır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейменувати теги" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重命名标签" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重新命名標籤" } } } }, "Repository not found" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Repository not found" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Repozitář nenalezen" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Repository not found" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Repository not found" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dépôt non trouvé" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Repository not found" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "रिपोजिटरी नहीं मिली" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Repository not found" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Repository not found" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Repository not found" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Repository not found" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Repositório não encontrado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Repository not found" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Репозиторий не найден" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Depo bulunamadı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Репозиторій не знайдено" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Repository not found" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "找不到儲存庫" } } } }, "Reveal in Finder" : { "comment" : "File Menu\nMenu Library", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فتح المجلد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit ve Finderu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In Finder anzeigen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar en el Finder" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Localiser dans le Finder" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג ב-Finder" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Finder में दिखाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra nel Finder" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Finderに表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Finder에서 보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon in Finder" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar no Finder" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Realçar no Finder" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать в Finder" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Finder'da göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати в Finder" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在Finder中显示" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示於 Finder" } } } }, "Search" : { "localizations" : { "es" : { "stringUnit" : { "state" : "translated", "value" : "Buscar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Поиск" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ara" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Пошук" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "搜尋" } } } }, "Search and create" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "بحث و انشاء" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat a vytvořit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Suchen und ersetzen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Buscar y crear" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Rechercher et créer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חיפוש ויצירה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खोजें और बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cerca e crea" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "検索または新規作成" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "검색 및 추가" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zoek en creëer" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Busque e crie" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar e criar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Найти и создать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ara ve oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Знайти або створити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "搜索或创建(按下回车即可创建)" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "搜尋並建立" } } } }, "Search and Create" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "بحث وانشاء" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat a vytvořit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Suchen und ersetzen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Buscar y crear" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Rechercher et créer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חיפוש יצירה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खोजें और बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cerca e crea" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "検索または新規作成" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "검색 및 추가" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zoek en creëer" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar e criar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar e criar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Найти и создать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ara ve oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Знайти або створити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "搜索或创建(按下回车即可创建)" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "搜尋並建立" } } } }, "Settings" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "التفضيلات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nastavení" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einstellungen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Preferencias" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Paramètres" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העדפות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्राथमिकताएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Preferenze" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "設定" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "환경설정" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voorkeuren" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Configurações" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Preferências" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Настройки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ayarlar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Налаштування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "偏好设置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "設定" } } } }, "Show in Finder" : { "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فتح المجلد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit ve Finderu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner anzeigen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Localiser dans le Finder" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג ב-Finder" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Finder में दिखाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra nel Finder" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Finderに表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Finder에서 보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon in Finder" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar no Finder" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar no Finder" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать в Finder" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bulucuda Göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати в Finder" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在访达中显示" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在Finder中顯示" } } } }, "Show Note List" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إظهار قائمة الملاحظات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit seznam poznámek" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notizliste anzeigen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar notas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher la liste des notes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג רשימת הערות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट सूची दिखाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra l'elenco delle note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ノート一覧を表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "메모 목록 표시" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Tonen notitielijst" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar lista de notas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar lista de notas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать список заметок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Not Listesini Göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати нотатки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "显示笔记列表" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示筆記列表" } } } }, "Show Options" : { "comment" : "Menu Library", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عرض الاعدادات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit možnosti" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einstellungen anzeigen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ver configuración" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher les options d'affichage" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג אפשרויות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "विकल्प दिखाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra opzioni vista" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "オプションを表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "보기 옵션" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon weergaveopties" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar as opções" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Desbloquear pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать опции" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seçenekleri Göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати параметри" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "显示视图选项" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示選項" } } } }, "Show Sidebar" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إظهار الشريط الجانبي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit boční panel" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Seitenleiste anzeigen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar barra lateral" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher la barre latérale" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג סרגל צד" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "साइडबार दिखाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra sidebar" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サイドバーを表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "사이드바 표시" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Tonen zijbalk" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar menu lateral" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar barra lateral" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать сайдбар" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kenar Çubuğunu Göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати сайдбар" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "显示侧边栏" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示側邊欄" } } } }, "Show view options" : { "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عرض الاعدادات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Volby zobrazení" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einstellungen anzeigen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ver configuración" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher les options d'affichage" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג אפשרויות תצוגה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "दृश्य विकल्प दिखाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra opzioni vista" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "表示オプションを表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "보기 옵션" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon weergaveopties" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar opções de visualização" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar opções de visualização" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Настроить вид" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Görünüm seçeneklerini göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Налаштування вигляду" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "显示视图选项" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示方式選項" } } } }, "Sort by" : { "comment" : "View menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "صنف حسب" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Řadit podle" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sortiere nach" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ordenar por" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Trier par" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מיין לפי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इसके अनुसार क्रमबद्ध करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ordina per" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "表示順序" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다음으로 정렬" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Sorteer op" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ordernar por" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ordenar por" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сортировать по" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sırala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сортувати за" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "排序方式" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "排序方式" } } } }, "SSH error" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "SSH خطأ" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Chyba SSH" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "SSH fehler" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "SSH error" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Erreur SSH" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "SSH שְׁגִיאָה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "SSH त्रुटि" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "SSH errore" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "SSH エラー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "SSH 오류" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "SSH fout" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "SSH error" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "SSH erro" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ошибка SSH" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "SSH hatası" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "SSH помилка" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "SSH error" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "SSH 錯誤" } } } }, "Tags" : { "comment" : "Sidebar label", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الوسوم" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Značky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Tags" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Etiquetas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Étiquettes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תגים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टैग" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Tag" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タグ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "태그" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Tags" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tags" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Etiquetas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Теги" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etiketler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Теги" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "标签" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "標籤" } } } }, "This action cannot be undone." : { "comment" : "Delete menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "هذا الاجراء غير قابل للرجوع" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Tuto akci nelze odvolat." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Diese Aktion kann nicht rückgängig gemacht werden" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Esta operación no se puede deshacer." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Cette action ne peut pas être annulée." } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "לא ניתן לבטל פעולה זו." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "यह क्रिया पूर्ववत नहीं की जा सकती।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Questa azione non può essere annullata." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "このアクションは取り消しできません。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이 작업은 취소할 수 없습니다." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Deze actie kan niet ongedaan gemaakt worden." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Essa ação não pode ser desfeita." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Esta ação não pode ser revertida." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Это действие не может быть отменено." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bu eylem geri alınamaz." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Цю дію неможливо відмінити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "此操作无法撤消。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "此動作無法復原。" } } } }, "Todo" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "قائمة المهام" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Úkoly" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Todo" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Pendientes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Tâches" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מטלות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टुडू" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Da Fare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タスク" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "할 일" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Te Doen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "A fazer" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tarefa" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Задачи" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yapılacak" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Завдання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "待办事项" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "待辦事項" } } } }, "Toggle preview" : { "extractionState" : "manual", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تبديل معاينة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přepnout náhled" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Vorschau umschalten" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cambiar previsualización" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Activer/désactiver l'aperçu" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג/הסתר תצוגה מקדימה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पूर्वावलोकन टॉगल करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Attiva / disattiva anteprima" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "プレビューを表示/非表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "미리보기 토글" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wissel voorvertoning" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Alterar pré-visualização" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Alternar pré-visualização" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переключить предпросмотр" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Önizlemeyi aç/kapat" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Переключити перегляд" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "切换预览" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "切換預覽" } } } }, "Trash" : { "comment" : "Sidebar label", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الملهملات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Koš" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Papierkorb" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Papelera" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Corbeille" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פח אשפה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कूडा" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cestino" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ゴミ箱" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "휴지통" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Prullenmand" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Lixo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Lixo" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Корзина" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Çöp Kutusu" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сміття" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "回收站" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "垃圾桶" } } } }, "Unlink External Folder" : { "comment" : "Menu Library", "localizations" : { "es" : { "stringUnit" : { "state" : "translated", "value" : "Desvincular carpeta externa" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Отвязать внешнюю папку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Harici Klasörü Bağlantısını Kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Від'єднати зовнішню папку" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "解除外部資料夾連結" } } } }, "Unlock" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الغاء القفل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odemknout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Freischalten" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Desbloquear" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פתח" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अनलॉक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sbloccare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ロック解除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "잠금 해제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Ontgrendelen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Desbloqueado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Desbloquear" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Разблокировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kilidi Aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розблокувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "解锁" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "解鎖" } } } }, "Unlock Folder" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فتح المجلد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odemknout složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner entsperren" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Desbloquear carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déverrouiller le dossier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "בטל נעילת תיקייה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ोल्डर अनलॉक करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sblocca cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォルダロック解除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "폴더 잠금 해제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Map ontgrendelen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Desbloquear pasta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Desbloquear pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Разблокировать директорию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasörü Kilidini Aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розблокувати директорію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "解锁文件夹" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "解鎖資料夾" } } } }, "Unpin" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إلغاء التثبيت" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odepnout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Loslösen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Desanclar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Désépingler" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "בטל הצמדה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अनपिन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sblocc" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ピン固定の解除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "고정 해제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "VerwijderPin" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Desafixar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Soltar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открепить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sabitlemeyi kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відкріпити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "取消置顶" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "取消釘選" } } } }, "Untagged" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "بدون علامات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Neoznačené" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ungetaggt" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Sin etiquetar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Non étiqueté" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ללא תגים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बिना टैग" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Senza Tag" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タグ無し" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "태그가 없는" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Niet gelabeld" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Sem tags" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Sem etiqueta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Без тегов" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etiketsiz" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Без тегів" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无标签" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "未標籤 " } } } }, "Untitled Note" : { "comment" : "Untitled Note", "extractionState" : "migrated", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ملاحظة بدون عنوان" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Poznámka bez názvu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Unbenannte Notiz" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nota sin título" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Note sans titre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פתק ללא שם" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक रहित नोट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nota senza titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "名称未設定のノート" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "무제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Naamloze notitie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nota sem título" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nota sem título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Без названия" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlıksız Not" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Без назви" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "未命名筆記" } } } }, "Update Web Page" : { "comment" : "File Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تحديث صفحة الويب" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Aktualizovat webovou stránku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Geteilte aktualisieren" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Actualizar página web" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mettre à jour la page Web" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עדכן את דף האינטרנט" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब पेज अपडेट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Aggiorna la pagina web" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Web ページの更新" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "웹 페이지 업데이트" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Webpagina bijwerken" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Update página Web" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Atualizar página da Web" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Обновить веб-страницу" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web Sayfasını Güncelle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Оновити веб-сторінку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更新网页" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "更新網頁" } } } }, "Upload error" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Upload error" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Chyba při nahrávání" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Upload error" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Upload error" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Erreur d'envoi de données" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Upload error" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अपलोड त्रुटि" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Upload error" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Upload error" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Upload error" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Upload error" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Upload error" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Upload error" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ошибка загрузки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yükleme hatası" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Upload error" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "上传错误" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "上傳錯誤" } } } }, "URL has been copied to clipboard" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تم نسخ الرابط" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Adresa URL zkopírována do schránky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "URL wurde in die Zwischenablage kopiert" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "El enlace se ha copiado al portapapeles" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "L'URL a été copiée dans le presse-papiers" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הכתובת הועתקה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "URL को क्लिपबोर्ड पर कॉपी कर दिया गया है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "L'URL è stato copiato negli appunti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "クリップボードにURLがコピーされました" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "URL이 클립보드에 복사되었습니다" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "URL is gekopieerd naar klembord" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "URL copiado para a área de transferência" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Endereço URL foi copiado para a área de transferência" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ссылка была скопирована в буфер обмена" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "URL panoya kopyalandı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Посилання було скопійовано в буфер обміну" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "URL已复制到剪贴板" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "網址已複製到剪貼簿" } } } }, "Verify Password:" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اكد كلمة المرور:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Ověřit heslo:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passwort bestätigen:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Verificar contraseña:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Vérifier le mot de passe :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אמת את הסיסמה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासवर्ड को सत्यापित करें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Verifica password:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスワードの確認:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "비밀번호 확인:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wachtwoord verifiëren:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Verifique a senha:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Verificar palavra-passe:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Повторить пароль:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Şifreyi Doğrula:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Підтвердіть пароль:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "验证密码:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "驗證密碼:" } } } }, "View" : { "comment" : "Menu", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عرض" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazení" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ansicht" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Visualización" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Voir" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תצוגה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "देखें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Vista" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Weergave" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Visualização" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Vista" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вид" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Görünüm" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вид" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "视图" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "檢視" } } } }, "We are detect that you are install FSNotes from Mac App Store with default storage in iCloud Drive, do you want to move old database in iCloud Drive?" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اكتشفنا أنك تقوم بتثبيت FSNotes من Mac App Store مع التخزين الافتراضي في iCloud Drive ، هل تريد نقل قاعدة البيانات القديمة في iCloud Drive؟" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Detekovali jsme, že jste nainstalovali FSNotes z Mac App Storu s výchozím úložištěm na iCloud Drive, chcete přesunout starou databázi na iCloud Drive?" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Wir stellen fest, dass Sie FSNotes aus dem Mac App Store mit Standardspeicher in iCloud Drive installiert haben. Möchten Sie die alte Datenbank in iCloud Drive verschieben?" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "We have detected that you have installed FSNotes from Mac App Store with default storage in iCloud Drive, do you want to move old database into iCloud Drive" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Se ha detectado que la versión de FSNotes instalada es del Mac App Store, con almacenamiento por defecto en iCloud Drive. ¿Quieres mover la base de datos antigua a iCloud Drive?" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nous détectons que vous installez FSNotes depuis le Mac App Store avec le stockage par défaut dans iCloud Drive, voulez-vous déplacer l'ancienne base de données dans iCloud Drive ?" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שמנו לב שהתקנת FSNotes מה-App Store עם אחסון ב-iCloud Drive. האם ברצונך להעביר את המאגר הקודם ל-iCloud Drive?" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हमने पाया है कि आपने iCloud ड्राइव में डिफ़ॉल्ट स्टोरेज के साथ Mac App Store से FSNotes इंस्टॉल किया है, क्या आप पुराने डेटाबेस को iCloud ड्राइव में ले जाना चाहते हैं?" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Abbiamo rilevato che stai installando FSNotes dal Mac App Store con l'archiviazione predefinita in iCloud Drive, vuoi spostare il vecchio database in iCloud Drive?" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FSNotesをMac App Storeからインストールし、iCloud Driveにデフォルトで保存していることがわかりますが、iCloud Driveにある古いデータベースを移動しますか?" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive에 기본 저장 공간이 있는 Mac App Store에서 FSNotes를 설치한 것으로 감지되었습니다. iCloud Drive에서 이전 데이터베이스를 옮기시겠습니까?" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "We hebben gedetecteerd dat je FSNotes hebt geïnstalleerd vanuit Mac App Store met standaard opslag in iCloud Drive. Wil je oude database verplaatsen naar iCloud Drive?" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Detectamos que está instalando o FSNotes da Mac App Store com armazenamento padrão no iCloud Drive, deseja mover o banco de dados antigo no iCloud Drive?" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Detectamos que está a instalar o FSNotes através da Mac App Store com o armazenamento por defeito no iCloud Drive, deseja mover a base de dados antiga para o iCloud Drive?" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Мы обнаружили, что вы установили FSNotes из Mac App Store с хранилищем по умолчанию в iCloud Drive. Хотите ли вы перенести старую базу данных в iCloud Drive?" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes'u Mac App Store'dan iCloud Drive'daki varsayılan depolama alanıyla yüklediğinizi tespit ettik, eski veritabanını iCloud Drive'a taşımak ister misiniz?" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ми виявили, що ви встановили FSNotes з Mac App Store із зберіганням за замовчуванням в iCloud Drive, хочете перенести стару базу даних в iCloud Drive?" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "我们检测到您是从 Mac App Store 安装的 FSNotes,数据默认存储在 iCloud 云盘,您想移动 iCloud 云盘中的旧数据库吗?" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "我們偵測到您從 Mac App Store 安裝了 FSNotes,且預設儲存空間為 iCloud 雲碟 。您是否要將舊的資料庫移動至iCloud 雲碟 ?" } } } }, "We can not move \"{DST_PATH}\" because this item already exist in selected destination." : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "لا يمكننا نقل \"{DST_PATH}\" لأن هذا العنصر موجود بالفعل في الوجهة المحددة." } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nelze přesunout „{DST_PATH}“, protože tato položka už ve vybraném cíli existuje." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "We can not move \"{DST_PATH}\" because this item already exist in selected destination." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No se puede mover \"{DST_PATH}\" porque ya existe en el destino seleccionado." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nous ne pouvons pas déplacer \"{DST_PATH}\" car cet élément existe déjà dans la destination sélectionnée." } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "לא ניתן להעביר \\\"{DST_PATH}\\\" מפני שקובץ באותו שם כבר קיים ביעד הנבחר." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हम \"{DST_PATH}\" को स्थानांतरित नहीं कर सकते क्योंकि यह आइटम पहले से ही चयनित गंतव्य में मौजूद है।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non possiamo spostare \"{DST_PATH}\" perché questo elemento esiste già nella destinazione selezionata." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "移動先に同名のアイテムがすでに存在しているため、\\\"{DST_PATH}\\\"を移動できませんでした" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이 항목이 선택한 대상에 이미 있으므로 \"{DST_PATH}\"을(를) 이동할 수 없습니다." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "We can not move \"{DST_PATH}\" because this item already exist in selected destination." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Não podemos mover \"{DST_PATH}\" porque este item já existe no destino selecionado." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "É impossível mover \"{DST_PATH}\" porque este item já existe atualmente no destino seleccionado." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Мы не можем переместить \\\\\\\"{DST_PATH}\\\\\\\", потому что этот путь уже существует в выбранном месте назначения." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "\"{DST_PATH}\" öğesini taşıyamayız çünkü bu öğe seçili hedefte zaten mevcut." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ми не можемо перемістити \\\"{DST_PATH}\\\", оскільки цей елемент вже існує у вибраному сховищі." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "我们不能移动 \"{DST_PATH}\",因为这个项目已经存在于选定的目标处。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無法移動”{DST_PATH}”,因為選定的目的地已存在此項目。" } } } }, "Web" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "创建网页URL并打开" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "網頁" } } } }, "Web publishing error" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "خطأ في النشر على الويب" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Chyba zveřejnění na webu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Fehler bei der Webveröffentlichung" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Error de publicación web" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Erreur de publication Web" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שגיאת פרסום באינטרנט" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब प्रकाशन त्रुटि" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Errore di pubblicazione sul Web" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ウェブ公開エラー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "웹 게시 오류" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Fout bij webpublicatie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "error de publicação Web" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Erro de publicação na Web" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ошибка веб-публикации" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web yayınlama hatası" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Помилка веб-публікації" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "网络发布错误" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "網頁發布錯誤" } } } }, "Wrong password" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "كلمة مرور خاطئة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nesprávné heslo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Falsches Passwort" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Contraseña incorrecta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mauvais mot de passe" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "סיסמה שגויה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गलत पासवर्ड" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Password errata" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "間違ったパスワード" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "잘못된 비밀번호" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verkeerd wachtwoord" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Senha incorreta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Senha incorreta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Неправильный пароль" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yanlış şifre" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Неправильний пароль" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "密码错误" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "密碼錯誤" } } } }, "Wrong repeated password" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عدم تطابق كلمة المرور" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nesprávné opakované heslo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passwort stimmt nicht überein" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Repetición de contraseña incorrecta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mauvais mot de passe répété" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסיסמאות לא תואמות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गलत दोहराया गया पासवर्ड" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Password ripetuta errata" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "再入力されたパスワードが一致していません" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "틀린 암호가 반복되었습니다." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verkeerd herhaald wachtwoord" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Senha de verificação incorreta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Palavra-passe de confirmação errada" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Несоответствие паролей" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yanlış tekrarlanan şifre" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Паролі не співпадають" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重复密码输入不正确" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "確認密碼不符 / 再次輸入的密碼錯誤" } } } }, "Yes" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نعم" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Ano" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ja" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Si" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Oui" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "כן" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हाँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Si" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "はい" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "예" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Ja" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Sim" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Sim" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Да" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Evet" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Так" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "是的" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "是" } } } }, "You cannot move an already encrypted note to an encrypted directory. You must first decrypt the note and repeat the steps." : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "لا يمكنك نقل ملاحظة مشفرة بالفعل إلى دليل مشفر. يجب عليك أولاً فك تشفير الملاحظة وتكرار الخطوات." } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Již zašifrovanou poznámku nelze přesunout do zašifrovaného adresáře. Nejprve poznámku dešifrujte a pak to zkuste znovu." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sie können eine bereits verschlüsselte Notiz nicht in ein verschlüsseltes Verzeichnis verschieben. Sie müssen die Notiz zuerst entschlüsseln und die Schritte wiederholen." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No puedes mover una nota ya encriptada a un directorio encriptado. Primero debes desencriptar la nota y repetir los pasos." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Vous ne pouvez pas déplacer une note déjà chiffré vers un répertoire chiffré. Vous devez d'abord déchiffrer la note et répéter les étapes." } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "לא ניתן להעביר פתק שכבר מוצפן לספרייה מוצפנת. תחילה עליך לפענח את הפתק ולחזור על השלבים." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "आप पहले से एन्क्रिप्टेड नोट को एन्क्रिप्टेड डायरेक्टरी में नहीं ले जा सकते। आपको पहले नोट को डिक्रिप्ट करना होगा और चरणों को दोहराना होगा" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non è possibile spostare una nota già crittografata in una directory crittografata. È necessario prima decifrare la nota e ripetere i passaggi." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "すでに暗号化されたノートを暗号化されたディレクトリに移動することはできません。まずノートの暗号化を解除し、手順を繰り返す必要があります。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이미 암호화된 노트는 암호화된 디렉토리로 옮길 수 없습니다. 먼저 노트의 암호를 해독하고 단계를 반복하셔야 합니다." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Je kunt een reeds gecodeerde notitie niet verplaatsen naar een gecodeerde map. Je moet de notitie eerst decoderen en de stappen herhalen." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Não é possível mover uma nota já criptografada para um diretório criptografado. Você deve primeiro descriptografar a nota e repetir as etapas." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Não é possível mover uma nota já encriptada para um diretório encriptado. Deve primeiro desencriptar a nota e repetir os passos." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Нельзя переместить уже зашифрованную заметку в зашифрованный каталог. Необходимо сначала расшифровать заметку и повторить действия." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Zaten şifrelenmiş bir notu şifrelenmiş bir dizine taşıyamazsınız. Önce notu şifresini çözmeli ve adımları tekrarlamalısınız." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ви не можете перемістити вже зашифровану нотатку до зашифрованого каталогу. Ви повинні спочатку розшифрувати нотатку і повторити кроки." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法将已加密的备忘移动到加密目录。您必须首先解密备忘,然后重复上述步骤。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "您無法將已加密的筆記移動至已加密的資料夾。請先將該筆記解密,然後再試一次。" } } } } }, "version" : "1.0" } ================================================ FILE: FSNotes/MainWindow.swift ================================================ // // MainWindow.swift // FSNotes // // Created by Oleksandr Glushchenko on 11/2/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Cocoa class MainWindow: NSWindow { override func awakeFromNib() { super.awakeFromNib() guard UserDefaults.standard.object(forKey: "NSWindow Frame myMainWindow") == nil else { return } if let screenHeight = NSScreen.main?.frame.height, let screenWidth = NSScreen.main?.frame.width { let frame = self.frame let x = (screenWidth - frame.width) / 2 let rect = NSRect(x: x, y: frame.origin.y, width: frame.width, height: screenHeight) self.setFrame(rect, display: true) } } } ================================================ FILE: FSNotes/MainWindowController.swift ================================================ // // MainWindowController.swift // FSNotes // // Created by BUDDAx2 on 8/9/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import AppKit class MainWindowController: NSWindowController, NSWindowDelegate { let notesListUndoManager = UndoManager() public var lastWindowSize: NSRect? = nil override func windowDidLoad() { AppDelegate.mainWindowController = self self.window?.hidesOnDeactivate = UserDefaultsManagement.hideOnDeactivate self.window?.titleVisibility = .hidden self.window?.titlebarAppearsTransparent = true self.windowFrameAutosaveName = "myMainWindow" } func windowDidResize(_ notification: Notification) { refreshEditArea() } func makeNew() { window?.makeKeyAndOrderFront(self) NSApp.activate(ignoringOtherApps: true) refreshEditArea(focusSearch: true) } func refreshEditArea(focusSearch: Bool = false) { guard let vc = ViewController.shared() else { return } if vc.sidebarOutlineView.isFirstLaunch || focusSearch { vc.search.window?.makeFirstResponder(vc.search) } else { vc.focusEditArea() } vc.editor.updateTextContainerInset() } func windowWillReturnUndoManager(_ window: NSWindow) -> UndoManager? { guard let fr = window.firstResponder else { return notesListUndoManager } if fr.isKind(of: NotesTableView.self) { return notesListUndoManager } if fr.isKind(of: EditTextView.self) { guard let vc = ViewController.shared(), let ev = vc.editor, ev.isEditable else { return notesListUndoManager } return vc.editorUndoManager } return notesListUndoManager } public static func shared() -> NSWindow? { return AppDelegate.mainWindowController?.window } func windowDidEnterFullScreen(_ notification: Notification) { UserDefaultsManagement.fullScreen = true } func windowDidExitFullScreen(_ notification: Notification) { UserDefaultsManagement.fullScreen = false } } ================================================ FILE: FSNotes/Model/StorageEntity+CoreDataClass.swift ================================================ // // StorageEntity+CoreDataClass.swift // // // Created by Oleksandr Glushchenko on 11/14/17. // // This file was automatically generated and should not be edited. // import Foundation import CoreData @objc(StorageEntity) public class StorageEntity: NSManagedObject { } ================================================ FILE: FSNotes/Model/StorageEntity+CoreDataProperties.swift ================================================ // // StorageEntity+CoreDataProperties.swift // // // Created by Oleksandr Glushchenko on 11/14/17. // // This file was automatically generated and should not be edited. // import Foundation import CoreData extension StorageEntity { @nonobjc public class func fetchRequest() -> NSFetchRequest { return NSFetchRequest(entityName: "StorageEntity") } @NSManaged public var name: String? @NSManaged public var path: String? } ================================================ FILE: FSNotes/NSWindowController+.swift ================================================ // // NSWindowController+.swift // FSNotes // // Created by Oleksandr Hlushchenko on 22.10.2022. // Copyright © 2022 Oleksandr Hlushchenko. All rights reserved. // import Cocoa extension NSWindowController { public static var lastWindowSize: NSRect? = nil public func maximizeWindow() { let currentSize = window?.frame if let screen = NSScreen.main { let size = NSWindowController.lastWindowSize ?? screen.visibleFrame window?.setFrame(size, display: true, animate: true) if NSWindowController.lastWindowSize == nil { NSWindowController.lastWindowSize = currentSize } else { NSWindowController.lastWindowSize = nil } } } } ================================================ FILE: FSNotes/NoteViewController.swift ================================================ // // NoteViewController.swift // FSNotes // // Created by Oleksandr Hlushchenko on 25.06.2022. // Copyright © 2022 Oleksandr Hlushchenko. All rights reserved. // import Foundation import AppKit class NoteViewController: EditorViewController, NSWindowDelegate { @IBOutlet weak var shareButton: NSButton! @IBOutlet weak var previewButton: NSButton! @IBOutlet weak var lockUnlockButton: NSButton! @IBOutlet weak var titleLabel: TitleTextField! @IBOutlet weak var editor: EditTextView! @IBOutlet weak var editorScrollView: EditorScrollView! @IBOutlet weak var titleBarView: TitleBarView! @IBOutlet weak var nonSelectedLabel: NSTextField! public func initWindow() { view.window?.title = "New note" view.window?.titleVisibility = .hidden view.window?.titlebarAppearsTransparent = true view.window?.backgroundColor = NSColor(named: "background_win") view.window?.delegate = self view.window?.setFrameOriginToPositionWindowInCenterOfScreen() editor.initTextStorage() editor.editorViewController = self editor.configure() vcEditor = editor vcTitleLabel = titleLabel vcNonSelectedLabel = nonSelectedLabel vcEditorScrollView = editorScrollView editor.updateTextContainerInset() super.initView() } func windowDidResize(_ notification: Notification) { editor.updateTextContainerInset() super.viewDidResize() } func windowWillClose(_ notification: Notification) { AppDelegate.noteWindows.removeAll(where: { ($0.contentViewController as? NoteViewController)?.editor.note === editor.note }) } func windowWillReturnUndoManager(_ window: NSWindow) -> UndoManager? { if let fr = window.firstResponder, fr.isKind(of: EditTextView.self), editor.isEditable { return editor.editorViewController?.editorUndoManager } return nil } } ================================================ FILE: FSNotes/Preferences/MasterPasswordViewController.swift ================================================ // // MasterPasswordViewController.swift // FSNotes // // Created by Oleksandr Glushchenko on 3/20/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Cocoa class MasterPasswordViewController: NSViewController { override func viewDidAppear() { hint.stringValue = UserDefaultsManagement.masterPasswordHint } @IBOutlet weak var hint: NSTextField! @IBOutlet weak var currentPassword: NSSecureTextField! @IBOutlet weak var newPassword: NSSecureTextField! @IBOutlet weak var repeatedPassword: NSSecureTextField! @IBAction func close(_ sender: Any) { dismiss(self) } @IBAction func change(_ sender: Any) { var password = String() do { let item = KeychainPasswordItem(service: KeychainConfiguration.serviceName, account: "Master Password") password = try item.readPassword() } catch { print(error) } if password.count > 0, currentPassword.stringValue != password { wrongCurrentPassword() return } if newPassword.stringValue != repeatedPassword.stringValue { wrongRepeatAlert() return } if newPassword.stringValue.count == 0 { emptyPassword() return } let item = KeychainPasswordItem(service: KeychainConfiguration.serviceName, account: "Master Password") do { try item.savePassword(newPassword.stringValue) } catch { print("Master password saving error: \(error)") } UserDefaultsManagement.masterPasswordHint = hint.stringValue dismiss(self) } private func wrongRepeatAlert() { let alert = NSAlert() alert.alertStyle = .critical alert.informativeText = NSLocalizedString("Please try again", comment: "") alert.messageText = NSLocalizedString("Wrong repeated password", comment: "") alert.runModal() } private func wrongCurrentPassword() { let alert = NSAlert() alert.alertStyle = .critical alert.informativeText = NSLocalizedString("Please try again", comment: "") alert.messageText = NSLocalizedString("Current password does not match with password in keychain", comment: "") alert.runModal() } private func emptyPassword() { let alert = NSAlert() alert.alertStyle = .critical alert.informativeText = NSLocalizedString("Please try again", comment: "") alert.messageText = NSLocalizedString("Empty password", comment: "") alert.runModal() } } ================================================ FILE: FSNotes/Preferences/PreferencesAdvancedViewController.swift ================================================ // // PreferencesAdvancedViewController.swift // FSNotes // // Created by Oleksandr Glushchenko on 3/17/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Cocoa class PreferencesAdvancedViewController: NSViewController { override func viewWillAppear() { super.viewWillAppear() } @IBOutlet weak var languagePopUp: NSPopUpButton! @IBOutlet weak var version: NSTextField! @IBOutlet weak var appearance: NSPopUpButton! @IBOutlet weak var appearanceLabel: NSTextField! @IBOutlet weak var dockIconFirst: NSButton! @IBOutlet weak var dockIconSecond: NSButton! @IBOutlet weak var trashPath: NSPathControl! @IBAction func appearanceClick(_ sender: NSPopUpButton) { if let type = AppearanceType(rawValue: sender.indexOfSelectedItem) { UserDefaultsManagement.appearanceType = type } restart() } override func viewDidAppear() { let languages = [ LanguageType(rawValue: 0), LanguageType(rawValue: 1), LanguageType(rawValue: 2), LanguageType(rawValue: 5), LanguageType(rawValue: 6), LanguageType(rawValue: 18), LanguageType(rawValue: 15), LanguageType(rawValue: 3), LanguageType(rawValue: 9), LanguageType(rawValue: 8), LanguageType(rawValue: 12), LanguageType(rawValue: 16), LanguageType(rawValue: 11), LanguageType(rawValue: 13), LanguageType(rawValue: 7), LanguageType(rawValue: 10), LanguageType(rawValue: 14), LanguageType(rawValue: 4), LanguageType(rawValue: 17) ] for language in languages { if let lang = language?.description, let id = language?.rawValue { languagePopUp.addItem(withTitle: lang) languagePopUp.lastItem?.state = (id == UserDefaultsManagement.defaultLanguage) ? .on : .off if id == UserDefaultsManagement.defaultLanguage { languagePopUp.selectItem(withTitle: lang) } } } if #available(OSX 10.14, *) { appearance.selectItem(at: UserDefaultsManagement.appearanceType.rawValue) } else { appearanceLabel.isHidden = true appearance.isHidden = true } if let dictionary = Bundle.main.infoDictionary, let ver = dictionary["CFBundleShortVersionString"] as? String, let build = dictionary["CFBundleVersion"] as? String { version.stringValue = "v\(ver) build \(build)" } switch UserDefaultsManagement.dockIcon { case 0: dockIconFirst.state = .on break case 1: dockIconSecond.state = .on break default: dockIconFirst.state = .on } if let url = Storage.shared().getDefaultTrash()?.url { trashPath.url = url } } @IBAction func languagePopUp(_ sender: NSPopUpButton) { let type = LanguageType.withName(rawValue: sender.title) UserDefaultsManagement.defaultLanguage = type.rawValue UserDefaults.standard.set([type.code], forKey: "AppleLanguages") UserDefaults.standard.synchronize() restart() } private func restart() { let url = URL(fileURLWithPath: Bundle.main.resourcePath!) let path = url.deletingLastPathComponent().deletingLastPathComponent().absoluteString let task = Process() task.launchPath = "/usr/bin/open" task.arguments = [path] task.launch() exit(0) } @IBAction func dockIcon(_ sender: NSButton) { UserDefaultsManagement.dockIcon = sender.tag guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { return } appDelegate.loadDockIcon() } @IBAction func trash(_ sender: NSButton) { let openPanel = NSOpenPanel() openPanel.directoryURL = Storage.shared().getDefaultTrash()?.url openPanel.allowsMultipleSelection = false openPanel.canChooseDirectories = true openPanel.canCreateDirectories = true openPanel.canChooseFiles = false openPanel.canSelectHiddenExtension = true openPanel.begin { (result) -> Void in if result == .OK { guard let url = openPanel.url else { return } let bookmarksManager = SandboxBookmark.sharedInstance() if let currentURL = UserDefaultsManagement.trashURL { bookmarksManager.remove(url: currentURL) } bookmarksManager.store(url: url) bookmarksManager.save() UserDefaultsManagement.trashURL = url self.trashPath.url = url Storage.shared().getDefaultTrash()?.url = url self.restart() } } } @IBAction func resetCaches(_ sender: Any) { if let sidebarTreeURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?.appendingPathComponent("sidebarTree") { try? FileManager.default.removeItem(at: sidebarTreeURL) } let projects = Storage.shared().getProjects() for project in projects { if let cacheUrl = project.getCacheURL() { try? FileManager.default.removeItem(at: cacheUrl) } project.isReadyForCacheSaving = false } restart() } @IBAction func resetSettings(_ sender: Any) { let store = NSUbiquitousKeyValueStore.default for (key, _) in store.dictionaryRepresentation { store.removeObject(forKey: key) } store.synchronize() if let bundleID = Bundle.main.bundleIdentifier { UserDefaults.standard.removePersistentDomain(forName: bundleID) UserDefaults.standard.synchronize() } if let userDefaultsURL = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first?.appendingPathComponent("Preferences").appendingPathComponent("co.fluder.FSNotes.plist") { try? FileManager.default.removeItem(at: userDefaultsURL) } if let editorsURL = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first?.appendingPathComponent("editors.settings") { try? FileManager.default.removeItem(at: editorsURL) } if let notesURL = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first?.appendingPathComponent("notes.settings") { try? FileManager.default.removeItem(at: notesURL) } if let bookmarkUrls = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first?.appendingPathComponent("Bookmarks.dict") { try? FileManager.default.removeItem(at: bookmarkUrls) } restart() } } ================================================ FILE: FSNotes/Preferences/PreferencesEditorViewController.swift ================================================ // // PreferencesEditorViewController.swift // FSNotes // // Created by Oleksandr Glushchenko on 3/17/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Cocoa class PreferencesEditorViewController: NSViewController { @IBOutlet weak var codeFontPreview: NSTextField! @IBOutlet weak var noteFontPreview: NSTextField! @IBOutlet weak var codeBlockHighlight: NSButton! @IBOutlet weak var markdownCodeTheme: NSPopUpButton! @IBOutlet weak var indentUsing: NSPopUpButton! @IBOutlet weak var inEditorFocus: NSButton! @IBOutlet weak var autocloseBrackets: NSButton! @IBOutlet weak var lineSpacing: NSSlider! @IBOutlet weak var imagesWidth: NSSlider! @IBOutlet weak var lineWidth: NSSlider! @IBOutlet weak var marginSize: NSSlider! @IBOutlet weak var inlineTags: NSButton! @IBOutlet weak var clickableLinks: NSButton! @IBOutlet weak var italicAsterisk: NSButton! @IBOutlet weak var italicUnderscore: NSButton! @IBOutlet weak var boldAsterisk: NSButton! @IBOutlet weak var boldUnderscore: NSButton! override func viewWillAppear() { super.viewWillAppear() preferredContentSize = NSSize(width: 550, height: 495) } override func viewDidAppear() { if let window = self.view.window { window.title = NSLocalizedString("Settings", comment: "") } codeBlockHighlight.state = UserDefaultsManagement.codeBlockHighlight ? NSControl.StateValue.on : NSControl.StateValue.off inEditorFocus.state = UserDefaultsManagement.focusInEditorOnNoteSelect ? NSControl.StateValue.on : NSControl.StateValue.off indentUsing.selectItem(at: UserDefaultsManagement.indentUsing) autocloseBrackets.state = UserDefaultsManagement.autocloseBrackets ? .on : .off markdownCodeTheme.selectItem(withTitle: UserDefaultsManagement.codeTheme.getName()) lineSpacing.floatValue = Float((UserDefaultsManagement.lineHeightMultiple - 1) * 10) imagesWidth.floatValue = UserDefaultsManagement.imagesWidth lineWidth.floatValue = UserDefaultsManagement.lineWidth marginSize.floatValue = UserDefaultsManagement.marginSize inlineTags.state = UserDefaultsManagement.inlineTags ? .on : .off clickableLinks.state = UserDefaultsManagement.clickableLinks ? .on : .off setCodeFontPreview() setNoteFontPreview() italicAsterisk.state = UserDefaultsManagement.italic == "*" ? .on : .off italicUnderscore.state = UserDefaultsManagement.italic == "_" ? .on : .off boldAsterisk.state = UserDefaultsManagement.bold == "**" ? .on : .off boldUnderscore.state = UserDefaultsManagement.bold == "__" ? .on : .off } //MARK: global variables let storage = Storage.shared() @IBAction func codeBlockHighlight(_ sender: NSButton) { UserDefaultsManagement.codeBlockHighlight = (sender.state == NSControl.StateValue.on) Storage.shared().resetCacheAttributes() let editors = AppDelegate.getEditTextViews() for editor in editors { if let evc = editor.editorViewController { evc.refillEditArea(force: true) } } } @IBAction func markdownCodeThemeAction(_ sender: NSPopUpButton) { guard let item = sender.selectedItem else { return } Storage.shared().resetCacheAttributes() if let theme = EditorTheme(themeName: item.title) { UserDefaultsManagement.codeTheme = theme } let editors = AppDelegate.getEditTextViews() for editor in editors { if let evc = editor.editorViewController { editor.textStorage?.updateParagraphStyle() MPreviewView.template = nil NotesTextProcessor.resetCaches() evc.refillEditArea(force: true) } } } @IBAction func inEditorFocus(_ sender: NSButton) { UserDefaultsManagement.focusInEditorOnNoteSelect = (sender.state == .on) } @IBAction func autocloseBrackets(_ sender: NSButton) { UserDefaultsManagement.autocloseBrackets = (sender.state == .on) } @IBAction func lineSpacing(_ sender: NSSlider) { UserDefaultsManagement.editorLineSpacing = 1 UserDefaultsManagement.lineHeightMultiple = CGFloat(1 + sender.floatValue / 10) let editors = AppDelegate.getEditTextViews() for editor in editors { if let evc = editor.editorViewController { MPreviewView.template = nil NotesTextProcessor.resetCaches() if let lm = evc.vcEditor?.layoutManager as? LayoutManager { lm.lineHeightMultiple = CGFloat(UserDefaultsManagement.lineHeightMultiple) lm.refreshLayoutSoftly() } } } } @IBAction func imagesWidth(_ sender: NSSlider) { UserDefaultsManagement.imagesWidth = sender.floatValue var temporary = URL(fileURLWithPath: NSTemporaryDirectory()) temporary.appendPathComponent("ThumbnailsBig") try? FileManager.default.removeItem(at: temporary) let editors = AppDelegate.getEditTextViews() for editor in editors { if let note = editor.note, let evc = editor.editorViewController { NotesTextProcessor.highlight(attributedString: note.content) evc.disablePreview() evc.refillEditArea() } } } @IBAction func lineWidth(_ sender: NSSlider) { UserDefaultsManagement.lineWidth = sender.floatValue let editors = AppDelegate.getEditTextViews() for editor in editors { if let evc = editor.editorViewController { editor.updateTextContainerInset() MPreviewView.template = nil NotesTextProcessor.resetCaches() evc.refillEditArea(force: true) } } } private func restart() { let url = URL(fileURLWithPath: Bundle.main.resourcePath!) let path = url.deletingLastPathComponent().deletingLastPathComponent().absoluteString let task = Process() task.launchPath = "/usr/bin/open" task.arguments = [path] task.launch() exit(0) } @IBAction func indentUsing(_ sender: NSPopUpButton) { guard let item = sender.selectedItem else { return } UserDefaultsManagement.indentUsing = item.tag } @IBAction func marginSize(_ sender: NSSlider) { UserDefaultsManagement.marginSize = sender.floatValue let editors = AppDelegate.getEditTextViews() for editor in editors { if let evc = editor.editorViewController { editor.updateTextContainerInset() MPreviewView.template = nil NotesTextProcessor.resetCaches() evc.refillEditArea(force: true) } } } @IBAction func inlineTags(_ sender: NSButton) { UserDefaultsManagement.inlineTags = (sender.state == .on) guard let vc = ViewController.shared() else { return } Storage.shared().tags = [] for note in Storage.shared().noteList { note.tags = [] if UserDefaultsManagement.inlineTags { _ = note.scanContentTags() } } vc.sidebarOutlineView.reloadSidebar() } @IBAction func highlightLinks(_ sender: NSButton) { UserDefaultsManagement.clickableLinks = (sender.state == NSControl.StateValue.on) Storage.shared().resetCacheAttributes() let editors = AppDelegate.getEditTextViews() for editor in editors { if let evc = editor.editorViewController { evc.refillEditArea() } } } @IBAction func setCodeFont(_ sender: NSButton) { let fontManager = NSFontManager.shared fontManager.setSelectedFont(UserDefaultsManagement.codeFont, isMultiple: false) fontManager.orderFrontFontPanel(self) fontManager.target = self fontManager.action = #selector(changeCodeFont(_:)) } @IBAction func setNoteFont(_ sender: NSButton) { let fontManager = NSFontManager.shared fontManager.setSelectedFont(UserDefaultsManagement.noteFont, isMultiple: false) fontManager.orderFrontFontPanel(self) fontManager.target = self fontManager.action = #selector(changeNoteFont(_:)) } @IBAction func changeCodeFont(_ sender: Any?) { let fontManager = NSFontManager.shared let newFont = fontManager.convert(UserDefaultsManagement.codeFont) UserDefaultsManagement.codeFont = newFont NotesTextProcessor.codeFont = newFont ViewController.shared()?.reloadFonts() setCodeFontPreview() } @IBAction func changeNoteFont(_ sender: Any?) { let fontManager = NSFontManager.shared let newFont = fontManager.convert(UserDefaultsManagement.noteFont) UserDefaultsManagement.noteFont = newFont ViewController.shared()?.reloadFonts() setNoteFontPreview() } @IBAction func resetFont(_ sender: Any) { UserDefaultsManagement.fontName = nil UserDefaultsManagement.codeFontName = "Source Code Pro" ViewController.shared()?.reloadFonts() setCodeFontPreview() setNoteFontPreview() } @IBAction func changeItalic(_ sender: NSButton) { UserDefaultsManagement.italic = sender.identifier?.rawValue == "italicAsterisk" ? "*" : "_" italicAsterisk.state = sender.identifier?.rawValue == "italicAsterisk" ? .on : .off italicUnderscore.state = sender.identifier?.rawValue == "italicUnderscore" ? .on : .off } @IBAction func changeBold(_ sender: NSButton) { UserDefaultsManagement.bold = sender.identifier?.rawValue == "boldAsterisk" ? "**" : "__" boldAsterisk.state = sender.identifier?.rawValue == "boldAsterisk" ? .on : .off boldUnderscore.state = sender.identifier?.rawValue == "boldUnderscore" ? .on : .off } private func setCodeFontPreview() { let familyName = UserDefaultsManagement.codeFont.familyName ?? "Source Code Pro" codeFontPreview.font = NSFont(name: familyName, size: 13) codeFontPreview.stringValue = "\(familyName) \(UserDefaultsManagement.codeFont.pointSize)pt" } private func setNoteFontPreview() { noteFontPreview.font = NSFont(name: UserDefaultsManagement.noteFont.fontName, size: 13) if let familyName = UserDefaultsManagement.noteFont.familyName { noteFontPreview.stringValue = "\(familyName) \(UserDefaultsManagement.noteFont.pointSize)pt" } } } ================================================ FILE: FSNotes/Preferences/PreferencesGeneralViewController.swift ================================================ // // PreferencesGeneralViewController.swift // FSNotes // // Created by Oleksandr Glushchenko on 3/17/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Cocoa import MASShortcut import CoreData class PreferencesGeneralViewController: NSViewController, NSTextFieldDelegate { override func viewWillAppear() { super.viewWillAppear() preferredContentSize = NSSize(width: 550, height: 481) } @IBOutlet var externalEditorApp: NSTextField! @IBOutlet var newNoteshortcutView: MASShortcutView! @IBOutlet var searchNotesShortcut: MASShortcutView! @IBOutlet var activateShortcut: MASShortcutView! @IBOutlet weak var quickNote: MASShortcutView! @IBOutlet weak var defaultStoragePath: NSPathControl! @IBOutlet weak var searchFocusOnESC: NSButton! @IBOutlet weak var defaultExtension: NSPopUpButton! @IBOutlet weak var fileContainer: NSPopUpButton! @IBOutlet weak var filesNaming: NSPopUpButton! @IBOutlet weak var automaticConflictsResolution: NSButton! @IBOutlet weak var saveTextBundleMetaData: NSButton! @IBOutlet weak var textMatchAutoSelection: NSButton! @IBOutlet weak var hideOnDeactivate: NSButton! //MARK: global variables let storage = Storage.shared() override func viewDidLoad() { super.viewDidLoad() initShortcuts() } override func viewDidAppear() { self.view.window!.title = NSLocalizedString("Settings", comment: "") externalEditorApp.stringValue = UserDefaultsManagement.externalEditor if let url = UserDefaultsManagement.storageUrl { defaultStoragePath.url = url } searchFocusOnESC.state = UserDefaultsManagement.shouldFocusSearchOnESCKeyDown ? .on : .off fileContainer.selectItem(withTag: UserDefaultsManagement.fileContainer.tag) filesNaming.selectItem(withTag: UserDefaultsManagement.naming.tag) let ext = UserDefaultsManagement.noteExtension defaultExtension.selectItem(withTitle: "." + ext) automaticConflictsResolution.state = UserDefaultsManagement.automaticConflictsResolution ? .on : .off saveTextBundleMetaData.state = UserDefaultsManagement.useTextBundleMetaToStoreDates ? .on : .off externalEditorApp.delegate = self textMatchAutoSelection.state = UserDefaultsManagement.textMatchAutoSelection ? .on : .off hideOnDeactivate.state = UserDefaultsManagement.hideOnDeactivate ? .on : .off } @IBAction func textMatchAutoSelection(_ sender: NSButton) { UserDefaultsManagement.textMatchAutoSelection = (sender.state == .on) } @IBAction func changeDefaultStorage(_ sender: Any) { let openPanel = NSOpenPanel() openPanel.directoryURL = UserDefaultsManagement.storageUrl openPanel.canChooseDirectories = true openPanel.canCreateDirectories = true openPanel.canChooseFiles = false openPanel.begin { (result) -> Void in if result == .OK { guard let url = openPanel.url else { return } guard let currentURL = UserDefaultsManagement.storageUrl else { return } let bookmarksManager = SandboxBookmark.sharedInstance() bookmarksManager.remove(url: currentURL) bookmarksManager.store(url: url) bookmarksManager.save() UserDefaultsManagement.storageType = .custom UserDefaultsManagement.customStoragePath = url.path self.defaultStoragePath.stringValue = url.path if let appDelegate = NSApplication.shared.delegate as? AppDelegate { let message = NSLocalizedString("Do you want to move current notes in the new destination?", comment: ""); appDelegate.promptToMoveDatabase(from: currentURL, to: url, messageText: message) } self.restart() } } } @IBAction func externalEditor(_ sender: Any) { UserDefaultsManagement.externalEditor = externalEditorApp.stringValue } @IBAction func searchFocusOnESC(_ sender: NSButton) { UserDefaultsManagement.shouldFocusSearchOnESCKeyDown = sender.state == .on } @IBAction func fileContainer(_ sender: NSPopUpButton) { guard let item = sender.selectedItem else { return } if let container = NoteContainer(rawValue: item.tag) { UserDefaultsManagement.fileContainer = container } } @IBAction func defaultExtension(_ sender: NSPopUpButton) { let ext = sender.title.replacingOccurrences(of: ".", with: "") UserDefaultsManagement.noteExtension = ext UserDefaultsManagement.fileFormat = .Markdown } @IBAction func filesNaming(_ sender: NSPopUpButton) { guard let item = sender.selectedItem else { return } if let naming = SettingsFilesNaming(rawValue: item.tag) { UserDefaultsManagement.naming = naming } } @IBAction func automaticConflictsResolution(_ sender: NSButton) { UserDefaultsManagement.automaticConflictsResolution = sender.state == .on } @IBAction func saveTextBundleMetaData(_ sender: NSButton) { UserDefaultsManagement.useTextBundleMetaToStoreDates = sender.state == .on } @IBAction func changeHideOnDeactivate(_ sender: NSButton) { UserDefaultsManagement.hideOnDeactivate = sender.state == .on // We don't need to set the user defaults value here as the checkbox is // bound to it. We do need to update each window's hideOnDeactivate. for window in NSApplication.shared.windows { if window.className == "NSStatusBarWindow" { continue } window.hidesOnDeactivate = UserDefaultsManagement.hideOnDeactivate } } func restart() { let url = URL(fileURLWithPath: Bundle.main.resourcePath!) let path = url.deletingLastPathComponent().deletingLastPathComponent().absoluteString let task = Process() task.launchPath = "/usr/bin/open" task.arguments = [path] task.launch() exit(0) } func initShortcuts() { guard let vc = ViewController.shared() else { return } let mas = MASShortcutMonitor.shared() newNoteshortcutView.shortcutValue = UserDefaultsManagement.newNoteShortcut searchNotesShortcut.shortcutValue = UserDefaultsManagement.searchNoteShortcut quickNote.shortcutValue = UserDefaultsManagement.quickNoteShortcut activateShortcut.shortcutValue = UserDefaultsManagement.activateShortcut newNoteshortcutView.shortcutValidator.allowAnyShortcutWithOptionModifier = true searchNotesShortcut.shortcutValidator.allowAnyShortcutWithOptionModifier = true quickNote.shortcutValidator.allowAnyShortcutWithOptionModifier = true activateShortcut.shortcutValidator.allowAnyShortcutWithOptionModifier = true newNoteshortcutView.shortcutValueChange = { (sender) in if ((self.newNoteshortcutView.shortcutValue) != nil) { mas?.unregisterShortcut(UserDefaultsManagement.newNoteShortcut) let keyCode = self.newNoteshortcutView.shortcutValue.keyCode let modifierFlags = self.newNoteshortcutView.shortcutValue.modifierFlags UserDefaultsManagement.newNoteShortcut = MASShortcut(keyCode: keyCode, modifierFlags: modifierFlags) MASShortcutMonitor.shared().register(self.newNoteshortcutView.shortcutValue, withAction: { vc.makeNoteShortcut() }) } else { mas?.unregisterShortcut(UserDefaultsManagement.newNoteShortcut) UserDefaultsManagement.newNoteShortcut = nil } } searchNotesShortcut.shortcutValueChange = { (sender) in if ((self.searchNotesShortcut.shortcutValue) != nil) { mas?.unregisterShortcut(UserDefaultsManagement.searchNoteShortcut) let keyCode = self.searchNotesShortcut.shortcutValue.keyCode let modifierFlags = self.searchNotesShortcut.shortcutValue.modifierFlags UserDefaultsManagement.searchNoteShortcut = MASShortcut(keyCode: keyCode, modifierFlags: modifierFlags) MASShortcutMonitor.shared().register(self.searchNotesShortcut.shortcutValue, withAction: { vc.searchShortcut() }) } else { mas?.unregisterShortcut(UserDefaultsManagement.searchNoteShortcut) UserDefaultsManagement.searchNoteShortcut = nil } } quickNote.shortcutValueChange = { (sender) in mas?.unregisterShortcut(UserDefaultsManagement.quickNoteShortcut) if ((self.quickNote.shortcutValue) != nil) { let keyCode = self.quickNote.shortcutValue.keyCode let modifierFlags = self.quickNote.shortcutValue.modifierFlags UserDefaultsManagement.quickNoteShortcut = MASShortcut(keyCode: keyCode, modifierFlags: modifierFlags) MASShortcutMonitor.shared().register(self.quickNote.shortcutValue, withAction: { vc.quickNote(self) }) } else { UserDefaultsManagement.quickNoteShortcut = nil } } activateShortcut.shortcutValueChange = { (sender) in mas?.unregisterShortcut(UserDefaultsManagement.activateShortcut) if ((self.activateShortcut.shortcutValue) != nil) { let keyCode = self.activateShortcut.shortcutValue.keyCode let modifierFlags = self.activateShortcut.shortcutValue.modifierFlags UserDefaultsManagement.activateShortcut = MASShortcut(keyCode: keyCode, modifierFlags: modifierFlags) MASShortcutMonitor.shared().register(self.activateShortcut.shortcutValue, withAction: { vc.searchShortcut(activate: true) }) } else { UserDefaultsManagement.activateShortcut = nil } } } func controlTextDidChange(_ notification: Notification) { guard let textField = notification.object as? NSTextField else { return } if textField.identifier?.rawValue == "openInExternalEditor" { UserDefaultsManagement.externalEditor = externalEditorApp.stringValue } } } ================================================ FILE: FSNotes/Preferences/PreferencesGitViewController.swift ================================================ // // PreferencesGitViewController.swift // FSNotes // // Created by Олександр Глущенко on 9/8/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Cocoa class PreferencesGitViewController: SettingsViewController { @IBOutlet weak var repositoriesPath: NSPathControl! @IBOutlet weak var snapshotsTextField: NSTextField! @IBOutlet weak var minutes: NSTextField! @IBOutlet weak var backupManually: NSButton! @IBOutlet weak var backupBySchedule: NSButton! @IBOutlet weak var pullInterval: NSTextField! @IBOutlet weak var separateDotGit: NSButton! @IBOutlet weak var askCommitMessage: NSButton! override func viewWillAppear() { super.viewWillAppear() //preferredContentSize = NSSize(width: 460, height: 579) loadGit(project: Storage.shared().getDefault()!) repositoriesPath.url = UserDefaultsManagement.gitStorage snapshotsTextField.stringValue = String(UserDefaultsManagement.snapshotsInterval) minutes.stringValue = String(UserDefaultsManagement.snapshotsIntervalMinutes) backupManually.state = UserDefaultsManagement.backupManually ? .on : .off backupBySchedule.state = UserDefaultsManagement.backupManually ? .off : .on pullInterval.stringValue = String(UserDefaultsManagement.pullInterval) separateDotGit.state = UserDefaultsManagement.separateRepo ? .on : .off askCommitMessage.state = UserDefaultsManagement.askCommitMessage ? .on : .off } @IBAction func changeGitStorage(_ sender: NSButton) { let openPanel = NSOpenPanel() openPanel.directoryURL = UserDefaultsManagement.gitStorage openPanel.allowsMultipleSelection = false openPanel.canChooseDirectories = true openPanel.canCreateDirectories = true openPanel.canChooseFiles = false openPanel.begin { (result) -> Void in if result == .OK { guard let url = openPanel.url?.standardized, url != UserDefaultsManagement.storageUrl else { let alert = NSAlert() alert.alertStyle = .critical alert.informativeText = NSLocalizedString("Path not available", comment: "") alert.messageText = NSLocalizedString("Default storage path should not be equal to Git path.", comment: "") alert.runModal() return } let bookmarksManager = SandboxBookmark.sharedInstance() if let currentURL = UserDefaultsManagement.gitStorage { bookmarksManager.remove(url: currentURL) } bookmarksManager.store(url: url) bookmarksManager.save() UserDefaultsManagement.gitStorage = url self.repositoriesPath.url = url } } } @IBAction func showFinder(_ sender: Any) { guard let storage = UserDefaultsManagement.gitStorage else { return } NSWorkspace.shared.activateFileViewerSelecting([storage]) } @IBAction func showTerminal(_ sender: Any) { guard let storage = UserDefaultsManagement.gitStorage else { return } NSWorkspace.shared.openFile(storage.path, withApplication: "Terminal.app") } @IBAction func backupMethod(_ sender: NSButton) { guard let ident = sender.identifier?.rawValue else { return } let isManualBackup = ident == "manual" UserDefaultsManagement.backupManually = isManualBackup backupManually.state = isManualBackup ? .on : .off backupBySchedule.state = isManualBackup ? .off : .on guard let vc = ViewController.shared() else { return } if backupBySchedule.state == .on { vc.schedulePull() } else { vc.stopPull() } } @IBAction func changeSnapshotIntervalByHours(_ sender: NSTextField) { if sender.stringValue == "0" || sender.stringValue.trim() == "" { sender.stringValue = "1" } if let interval = Int(sender.stringValue) { UserDefaultsManagement.snapshotsInterval = interval } guard let vc = ViewController.shared() else { return } vc.scheduleSnapshots() } @IBAction func changeSnapshotsIntervalByMinutes(_ sender: NSTextField) { if let interval = Int(sender.stringValue) { UserDefaultsManagement.snapshotsIntervalMinutes = interval } guard let vc = ViewController.shared() else { return } vc.scheduleSnapshots() } @IBAction func pullInterval(_ sender: NSTextField) { if var interval = Int(sender.stringValue) { if interval < 10 { interval = 10 pullInterval.stringValue = String(10) } UserDefaultsManagement.pullInterval = interval } guard let vc = ViewController.shared() else { return } vc.schedulePull() } @IBAction func separateRepo(_ sender: NSButton) { UserDefaultsManagement.separateRepo = sender.state == .on } @IBAction func askCommitMessage(_ sender: NSButton) { UserDefaultsManagement.askCommitMessage = sender.state == .on } } ================================================ FILE: FSNotes/Preferences/PreferencesSecurityViewController.swift ================================================ // // PreferencesSecurityViewController.swift // FSNotes // // Created by Oleksandr Glushchenko on 3/17/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Cocoa import LocalAuthentication class PreferencesSecurityViewController: NSViewController { @IBOutlet weak var lockOnSleep: NSButton! @IBOutlet weak var lockOnScreenActivated: NSButton! @IBOutlet weak var lockWhenFastUser: NSButton! @IBOutlet weak var allowTouchID: NSButton! @IBOutlet weak var masterPassword: NSButton! override func viewDidLoad() { lockOnSleep.state = UserDefaultsManagement.lockOnSleep ? .on : .off lockOnScreenActivated.state = UserDefaultsManagement.lockOnSleep ? .on : .off lockWhenFastUser.state = UserDefaultsManagement.lockOnUserSwitch ? .on : .off allowTouchID.state = UserDefaultsManagement.allowTouchID ? .on : .off allowTouchID.isEnabled = true masterPassword.isEnabled = UserDefaultsManagement.allowTouchID if #available(OSX 10.12.2, *) { let context = LAContext() if !context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) { disableTouchID() return } } else { disableTouchID() } } override func viewWillAppear() { super.viewWillAppear() preferredContentSize = NSSize(width: 550, height: 352) } @IBAction func openMasterPasswordWindow(_ sender: Any) { guard let vc = ViewController.shared() else { return } if let controller = vc.storyboard?.instantiateController(withIdentifier: "MasterPasswordViewController") as? MasterPasswordViewController { presentAsSheet(controller) } } @IBAction func lockOnSleep(_ sender: NSButton) { UserDefaultsManagement.lockOnSleep = (sender.state == .on) } @IBAction func lockOnScreenActivated(_ sender: NSButton) { UserDefaultsManagement.lockOnScreenActivated = (sender.state == .on) } @IBAction func lockWhenSwitched(_ sender: NSButton) { UserDefaultsManagement.lockOnUserSwitch = (sender.state == .on) } @IBAction func allowTouchID(_ sender: NSButton) { UserDefaultsManagement.allowTouchID = (sender.state == .on) masterPassword.isEnabled = UserDefaultsManagement.allowTouchID } private func disableTouchID() { masterPassword.isEnabled = false allowTouchID.isEnabled = false allowTouchID.state = .off } } ================================================ FILE: FSNotes/Preferences/PreferencesUserInterfaceViewController.swift ================================================ // // PreferencesUserInterfaceViewController.swift // FSNotes // // Created by Oleksandr Glushchenko on 3/17/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Cocoa class PreferencesUserInterfaceViewController: NSViewController { @IBOutlet weak var cellSpacing: NSSlider! @IBOutlet weak var previewFontSize: NSPopUpButton! @IBOutlet weak var hideImagesPreview: NSButton! @IBOutlet weak var hidePreview: NSButton! @IBOutlet weak var hideDate: NSButton! @IBOutlet weak var firstLineAsTitle: NSButton! @IBOutlet weak var horizontalOrientation: NSButton! @IBOutlet weak var showDockIcon: NSButton! @IBOutlet weak var showInMenuBar: NSButton! override func viewWillAppear() { super.viewWillAppear() preferredContentSize = NSSize(width: 550, height: 460) } override func viewDidAppear() { guard let window = self.view.window else { return } window.title = NSLocalizedString("Settings", comment: "") hidePreview.state = UserDefaultsManagement.hidePreview ? NSControl.StateValue.on : NSControl.StateValue.off cellSpacing.doubleValue = Double(UserDefaultsManagement.cellSpacing) showDockIcon.state = UserDefaultsManagement.showDockIcon ? .on : .off showInMenuBar.state = UserDefaultsManagement.showInMenuBar ? .on : .off previewFontSize.selectItem(withTag: UserDefaultsManagement.previewFontSize) hideImagesPreview.state = UserDefaultsManagement.hidePreviewImages ? .on : .off hideDate.state = UserDefaultsManagement.hideDate ? .on : .off firstLineAsTitle.state = UserDefaultsManagement.firstLineAsTitle ? .on : .off horizontalOrientation.state = UserDefaultsManagement .horizontalOrientation ? .on : .off } @IBAction func changeCellSpacing(_ sender: NSSlider) { guard let vc = ViewController.shared() else { return } vc.setTableRowHeight() } @IBAction func changePreview(_ sender: Any) { guard let vc = ViewController.shared() else { return } UserDefaultsManagement.hidePreview = ((sender as AnyObject).state == NSControl.StateValue.on) vc.notesTableView.reloadData() } @IBAction func hideImagesPreview(_ sender: NSButton) { UserDefaultsManagement.hidePreviewImages = sender.state == .on guard let vc = ViewController.shared() else { return } vc.notesTableView.reloadData() } @IBAction func changePreviewFontSize(_ sender: NSPopUpButton) { guard let tag = sender.selectedItem?.tag else { return } UserDefaultsManagement.previewFontSize = tag guard let vc = ViewController.shared() else { return } vc.notesTableView.reloadData() } @IBAction func hideDate(_ sender: NSButton) { UserDefaultsManagement.hideDate = (sender.state == .on) guard let vc = ViewController.shared() else { return } vc.notesTableView.reloadData() } @IBAction func firstLineAsTitle(_ sender: NSButton) { UserDefaultsManagement.firstLineAsTitle = (sender.state == .on) let storage = Storage.shared() for note in storage.noteList { note.invalidateCache() } guard let vc = ViewController.shared() else { return } vc.notesTableView.reloadData() } @IBAction func horizontalOrientation(_ sender: NSButton) { UserDefaultsManagement.horizontalOrientation = (sender.state == .on) let task = Process() task.launchPath = "/usr/bin/open" task.arguments = [Bundle.main.bundlePath] try? task.run() NSApp.terminate(nil) } @IBAction func showDockIcon(_ sender: NSButton) { let isEnabled = sender.state == .on UserDefaultsManagement.showDockIcon = isEnabled NSApp.setActivationPolicy(isEnabled ? .regular : .accessory) DispatchQueue.main.async { NSMenu.setMenuBarVisible(true) NSApp.activate(ignoringOtherApps: true) } } @IBAction func showInMenuBar(_ sender: NSButton) { UserDefaultsManagement.showInMenuBar = sender.state == .on guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { return } if sender.state == .off { appDelegate.removeMenuBar(nil) return } appDelegate.addMenuBar(nil) } } ================================================ FILE: FSNotes/Preferences/PreferencesWebViewController.swift ================================================ // // PreferencesWebViewController.swift // FSNotes // // Created by Oleksandr Hlushchenko on 20.08.2022. // Copyright © 2022 Oleksandr Hlushchenko. All rights reserved. // import Cocoa import Shout class PreferencesWebViewController: NSViewController, NSTextFieldDelegate { override func viewWillAppear() { super.viewWillAppear() preferredContentSize = NSSize(width: 550, height: 512) host.stringValue = UserDefaultsManagement.sftpHost port.stringValue = String(UserDefaultsManagement.sftpPort) path.stringValue = UserDefaultsManagement.sftpPath ?? "" web.stringValue = UserDefaultsManagement.sftpWeb ?? "" username.stringValue = UserDefaultsManagement.sftpUsername password.stringValue = UserDefaultsManagement.sftpPassword passphrase.stringValue = UserDefaultsManagement.sftpPassphrase if let accessData = UserDefaultsManagement.sftpAccessData, let bookmarks = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSDictionary.self, NSURL.self, NSData.self], from: accessData) as? [URL: Data] { for bookmark in bookmarks { rsaPath.url = bookmark.key break } } publishFSNotes.state = UserDefaultsManagement.customWebServer ? .off : .on publishCustom.state = UserDefaultsManagement.customWebServer ? .on : .off if !UserDefaultsManagement.customWebServer { toggleState(state: false) } username.delegate = self port.delegate = self path.delegate = self web.delegate = self username.delegate = self password.delegate = self passphrase.delegate = self } @IBOutlet weak var host: NSTextField! @IBOutlet weak var port: NSTextField! @IBOutlet weak var path: NSTextField! @IBOutlet weak var web: NSTextField! @IBOutlet weak var username: NSTextField! @IBOutlet weak var password: NSSecureTextField! @IBOutlet weak var rsaPath: NSPathControl! @IBOutlet weak var key: NSButton! @IBOutlet weak var passphrase: NSSecureTextField! @IBOutlet weak var publishFSNotes: NSButton! @IBOutlet weak var publishCustom: NSButton! @IBOutlet weak var uploadAndTest: NSButton! @IBAction func host(_ sender: NSTextField) { UserDefaultsManagement.sftpHost = sender.stringValue } @IBAction func port(_ sender: NSTextField) { if let port = Int32(sender.stringValue) { UserDefaultsManagement.sftpPort = port } } @IBAction func path(_ sender: NSTextField) { UserDefaultsManagement.sftpPath = sender.stringValue } @IBAction func web(_ sender: NSTextField) { UserDefaultsManagement.sftpWeb = sender.stringValue } @IBAction func username(_ sender: NSTextField) { UserDefaultsManagement.sftpUsername = sender.stringValue } @IBAction func password(_ sender: NSSecureTextField) { UserDefaultsManagement.sftpPassword = sender.stringValue } @IBAction func passphrase(_ sender: NSSecureTextField) { UserDefaultsManagement.sftpPassphrase = sender.stringValue } @IBAction func privateKey(_ sender: Any) { let openPanel = NSOpenPanel() openPanel.allowsMultipleSelection = true openPanel.canChooseFiles = true openPanel.begin { (result) -> Void in if result == .OK { if openPanel.urls.count != 2 { let alert = NSAlert() alert.alertStyle = .warning alert.informativeText = NSLocalizedString("Please select private and public key", comment: "") alert.runModal() return } var bookmarks = [URL: Data]() for url in openPanel.urls { do { let data = try url.bookmarkData(options: NSURL.BookmarkCreationOptions.withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil) bookmarks[url] = data } catch { print(error.localizedDescription) } } let data = try? NSKeyedArchiver.archivedData(withRootObject: bookmarks, requiringSecureCoding: true) UserDefaultsManagement.sftpAccessData = data self.rsaPath.url = openPanel.urls[0] } } } @IBAction func test(_ sender: Any) { var publicKeyURL: URL? var privateKeyURL: URL? if let accessData = UserDefaultsManagement.sftpAccessData, let bookmarks = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSDictionary.self, NSURL.self, NSData.self], from: accessData) as? [URL: Data] { for bookmark in bookmarks { if bookmark.key.path.hasSuffix(".pub") { publicKeyURL = bookmark.key } else { privateKeyURL = bookmark.key } } } let host = UserDefaultsManagement.sftpHost let port = UserDefaultsManagement.sftpPort let username = UserDefaultsManagement.sftpUsername let password = UserDefaultsManagement.sftpPassword let passphrase = UserDefaultsManagement.sftpPassphrase if password.count == 0, publicKeyURL == nil || publicKeyURL == nil { uploadError(text: "Please set private and public keys") return } let path = Bundle.main.path(forResource: "MPreview", ofType: ".bundle") let url = NSURL.fileURL(withPath: path!) let bundle = Bundle(url: url) guard let bundleResourceURL = bundle?.resourceURL else { uploadError(text: "Test bundle can not found") return } let localJsDir = bundleResourceURL.appendingPathComponent("js", isDirectory: true) let localFontsDir = bundleResourceURL.appendingPathComponent("fonts", isDirectory: true) let localCssFile = bundleResourceURL.appendingPathComponent("main.css") let alert = NSAlert() do { let ssh = try SSH(host: host, port: port) guard let remoteDir = UserDefaultsManagement.sftpPath else { throw "Please enter remote path" } let remoteJsDir = "\(remoteDir)js/" let remoteFontsDir = "\(remoteDir)fonts/" guard let files = try? FileManager.default.contentsOfDirectory(atPath: localJsDir.path) else { return } guard let fontFiles = try? FileManager.default.contentsOfDirectory(atPath: localFontsDir.path) else { return } if password.count > 0 { try ssh.authenticate(username: username, password: password) } else if let publicKeyURL = publicKeyURL, let privateKeyURL = privateKeyURL { try ssh.authenticate(username: username, privateKey: privateKeyURL.path, publicKey: publicKeyURL.path, passphrase: passphrase) } _ = try ssh.execute("mkdir -p \(remoteJsDir)") _ = try ssh.execute("mkdir -p \(remoteFontsDir)") let permissions = Permissions(arrayLiteral: .write, .read, .execute) let filePerm = FilePermissions(owner: permissions, group: permissions, others: permissions) let sftp = try ssh.openSftp() for file in files { let localURL = localJsDir.appendingPathComponent(file) try? sftp.upload(localURL: localURL, remotePath: remoteJsDir + file, permissions: filePerm) } for file in fontFiles { let localURL = localFontsDir.appendingPathComponent(file) try? sftp.upload(localURL: localURL, remotePath: remoteFontsDir + file, permissions: filePerm) } try sftp.upload(localURL: localCssFile, remotePath: remoteDir + "main.css", permissions: filePerm) alert.alertStyle = .informational alert.messageText = NSLocalizedString("Connection established successfully 🤟", comment: "") } catch let sshError as SSHError { alert.alertStyle = .critical alert.informativeText = sshError.description alert.messageText = NSLocalizedString("SSH error", comment: "") } catch { alert.alertStyle = .critical alert.informativeText = error.localizedDescription alert.messageText = NSLocalizedString("SSH error", comment: "") } alert.beginSheetModal(for: self.view.window!) } private func uploadError(text: String) { let alert = NSAlert() alert.alertStyle = .critical alert.informativeText = NSLocalizedString("Upload error", comment: "") alert.messageText = text alert.beginSheetModal(for: self.view.window!) } @IBAction func publishTo(_ sender: NSButton) { if sender.tag == 0 { publishCustom.state = .off toggleState(state: false) } else { publishFSNotes.state = .off toggleState(state: true) } UserDefaultsManagement.customWebServer = publishCustom.state == .on } public func toggleState(state: Bool) { host.isEnabled = state port.isEnabled = state path.isEnabled = state web.isEnabled = state username.isEnabled = state password.isEnabled = state passphrase.isEnabled = state uploadAndTest.isEnabled = state key.isEnabled = state } @IBAction func resetWebKeys(_ sender: NSButton) { UserDefaultsManagement.sftpAccessData = nil rsaPath.url = nil } func controlTextDidChange(_ notification: Notification) { guard let textField = notification.object as? NSTextField, let value = textField.identifier?.rawValue else { return } switch value { case "settingsWebHost": UserDefaultsManagement.sftpHost = host.stringValue case "settingsWebPort": UserDefaultsManagement.sftpPort = Int32(port.stringValue) ?? 22 case "settingsWebPath": UserDefaultsManagement.sftpPath = path.stringValue case "settingsWebWeb": UserDefaultsManagement.sftpWeb = web.stringValue case "settingsWebUsername": UserDefaultsManagement.sftpUsername = username.stringValue case "settingsWebPassword": UserDefaultsManagement.sftpPassword = password.stringValue case "settingsWebPassphrase": UserDefaultsManagement.sftpPassphrase = passphrase.stringValue default: break } } } extension String: LocalizedError { // Adds error.localizedDescription to Error instances public var errorDescription: String? { return self } } ================================================ FILE: FSNotes/Preferences/SettingsViewController.swift ================================================ // // SettingsViewController.swift // FSNotes // // Created by Oleksandr Hlushchenko on 14.03.2023. // Copyright © 2023 Oleksandr Hlushchenko. All rights reserved. // import AppKit class SettingsViewController: NSViewController, NSTextFieldDelegate { public var gitProject: Project? public var project: Project? public var progress: GitProgress? override func viewDidAppear() { passphrase.delegate = self origin.delegate = self } @IBOutlet weak var origin: NSTextField! @IBOutlet weak var keyStatus: NSTextField! @IBOutlet weak var logTextField: NSTextField! @IBOutlet weak var removeButton: NSButton! @IBOutlet weak var cloneButton: NSButton! @IBOutlet weak var passphrase: NSSecureTextField! @IBOutlet weak var progressIndicator: NSProgressIndicator! @IBAction func removeRepository(_ sender: Any) { gitProject?.removeRepository(progress: progress) updateButtons() } @IBAction func origin(_ sender: Any) { gitProject?.settings.setOrigin(origin.stringValue) gitProject?.saveSettings() updateButtons() } @IBAction func passphrase(_ sender: Any) { gitProject?.settings.gitPrivateKeyPassphrase = passphrase.stringValue gitProject?.saveSettings() } @IBAction func clonePull(_ sender: Any) { guard let project = self.gitProject else { return } if let origin = project.settings.gitOrigin, origin.startsWith(string: "https://") { let alert = NSAlert() alert.messageText = "Wrong configuration" alert.alertStyle = .critical alert.informativeText = "Please use ssh keys, https auth is not supported" alert.runModal() return } let action = project.getRepositoryState() updateButtons(isActive: true) ViewController.gitQueue.addOperation({ defer { ViewController.gitQueueOperationDate = nil ViewController.gitQueueBusy = false DispatchQueue.main.async { self.updateButtons(isActive: false) } } ViewController.gitQueueOperationDate = Date() ViewController.gitQueueBusy = true if let message = project.gitDo(action, progress: self.progress) { DispatchQueue.main.async { let alert = NSAlert() alert.alertStyle = .critical alert.informativeText = message alert.messageText = NSLocalizedString("git error", comment: "") alert.runModal() } } }) } @IBAction func privateKey(_ sender: Any) { let openPanel = NSOpenPanel() openPanel.allowsMultipleSelection = true openPanel.canChooseFiles = true openPanel.begin { (result) -> Void in if result == .OK { if openPanel.urls.count != 1 { let alert = NSAlert() alert.alertStyle = .warning alert.informativeText = NSLocalizedString("Please select private key", comment: "") alert.runModal() return } self.gitProject?.settings.gitPrivateKey = try? Data(contentsOf: openPanel.urls[0]) self.gitProject?.saveSettings() self.keyStatus.stringValue = "✅" } } } @IBAction func resetKey(_ sender: Any) { gitProject?.removeSSHKey() gitProject?.settings.gitPrivateKey = nil gitProject?.saveSettings() keyStatus.stringValue = "" } public func controlTextDidChange(_ notification: Notification) { guard let textField = notification.object as? NSTextField else { return } let id = textField.identifier?.rawValue if id == "gitOrigin" || id == "gitOriginMain" { gitProject?.settings.setOrigin(textField.stringValue) updateButtons() } if id == "gitPassphrase" || id == "gitPassphraseMain" { gitProject?.settings.gitPrivateKeyPassphrase = textField.stringValue } DispatchQueue.global(qos: .background).async { self.gitProject?.saveSettings() } } public func updateButtons(isActive: Bool? = nil) { guard let project = gitProject else { return } progressIndicator.isHidden = !project.isActiveGit cloneButton.title = project.getRepositoryState().title removeButton.isEnabled = project.hasRepository() if let isActive = isActive { if isActive { progressIndicator.startAnimation(nil) progressIndicator.isHidden = false } else { progressIndicator.stopAnimation(nil) progressIndicator.isHidden = true } } } public func loadGit(project: Project) { var project = project if project.isVirtual { if let defaultProject = Storage.shared().getDefault() { project = defaultProject } } self.gitProject = project origin.stringValue = project.settings.gitOrigin ?? "" passphrase.stringValue = project.settings.gitPrivateKeyPassphrase ?? "" keyStatus.stringValue = project.settings.gitPrivateKey != nil ? "✅" : "" updateButtons() progress = GitProgress(statusTextField: logTextField, project: project) // Global instance for libgit2 callbacks AppDelegate.gitProgress = progress if let status = project.gitStatus { logTextField.stringValue = status } } } ================================================ FILE: FSNotes/PrefsViewController.swift ================================================ // // PrefsViewController.swift // FSNotes // // Created by Oleksandr Glushchenko on 8/4/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Cocoa import MASShortcut import CoreData class PrefsViewController: NSTabViewController { @IBOutlet weak var generalTabViewItem: NSTabViewItem! @IBOutlet weak var libraryTabViewItem: NSTabViewItem! @IBOutlet weak var editorTabViewItem: NSTabViewItem! @IBOutlet weak var securityTabViewItem: NSTabViewItem! @IBOutlet weak var gitTabViewItem: NSTabViewItem! @IBOutlet weak var webTabViewItem: NSTabViewItem! @IBOutlet weak var advancedTabViewItem: NSTabViewItem! override func viewDidLoad() { self.title = NSLocalizedString("Settings", comment: "") super.viewDidLoad() if #available(macOS 11.0, *) { let general = NSImage.init(systemSymbolName: "gearshape", accessibilityDescription: nil) let library = NSImage.init(systemSymbolName: "sidebar.left", accessibilityDescription: nil) let editor = NSImage.init(systemSymbolName: "doc.richtext", accessibilityDescription: nil) let security = NSImage.init(systemSymbolName: "lock", accessibilityDescription: nil) let git = NSImage.init(systemSymbolName: "arrow.triangle.pull", accessibilityDescription: nil) let web = NSImage.init(systemSymbolName: "globe", accessibilityDescription: nil) let advanced = NSImage.init(systemSymbolName: "slider.vertical.3", accessibilityDescription: nil) if let general = general, let library = library, let editor = editor, let security = security, let git = git, let web = web, let advanced = advanced { generalTabViewItem.image = general libraryTabViewItem.image = library editorTabViewItem.image = editor securityTabViewItem.image = security gitTabViewItem.image = git webTabViewItem.image = web advancedTabViewItem.image = advanced } } } override func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? { let toolbarItem = super.toolbar(toolbar, itemForItemIdentifier: itemIdentifier, willBeInsertedIntoToolbar: flag) if let toolbarItem = toolbarItem, let tabViewItem = tabViewItems.first(where: { ($0.identifier as? String) == itemIdentifier.rawValue }) { if let name = tabViewItem.identifier as? String, name == "git" { toolbarItem.label = "\(tabViewItem.label) " return toolbarItem } if let name = tabViewItem.identifier as? String, !["advanced", "security"].contains(name) { toolbarItem.label = "\(tabViewItem.label) " } } return toolbarItem } } ================================================ FILE: FSNotes/PrefsWindowController.swift ================================================ // // PrefsWindowController.swift // FSNotes // // Created by Jeff Hanbury on 13/08/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Cocoa class PrefsWindowController: NSWindowController, NSWindowDelegate { override func windowDidLoad() { super.windowDidLoad() self.window?.delegate = self self.window?.title = NSLocalizedString("Settings", comment: "") } } ================================================ FILE: FSNotes/ProjectSettingsViewController.swift ================================================ // // ProjectSettingsViewController.swift // FSNotes // // Created by Oleksandr Glushchenko on 11/23/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa import Carbon.HIToolbox class ProjectSettingsViewController: SettingsViewController { @IBOutlet weak var modificationDate: NSButton! @IBOutlet weak var creationDate: NSButton! @IBOutlet weak var titleButton: NSButton! @IBOutlet weak var sortByGlobal: NSButton! @IBOutlet weak var directionASC: NSButton! @IBOutlet weak var directionDESC: NSButton! @IBOutlet weak var showInAll: NSButton! @IBOutlet weak var firstLineAsTitle: NSButton! @IBOutlet weak var nestedFoldersContent: NSButton! @IBOutlet weak var gitView: NSView! @IBOutlet weak var gitViewHeight: NSLayoutConstraint! override func viewDidLoad() { gitView.isHidden = true gitViewHeight.constant = 0 } @IBAction func sortBy(_ sender: NSButton) { guard let project = project else { return } let sortBy = SortBy(rawValue: sender.identifier!.rawValue)! project.settings.sortBy = sortBy project.saveSettings() guard let vc = ViewController.shared() else { return } vc.buildSearchQuery() vc.updateTable() } @IBAction func sortDirection(_ sender: NSButton) { guard let project = project else { return } project.settings.sortDirection = SortDirection(rawValue: sender.identifier!.rawValue)! project.saveSettings() guard let vc = ViewController.shared() else { return } vc.buildSearchQuery() vc.updateTable() } @IBAction func showNotesInMainList(_ sender: NSButton) { project?.settings.showInCommon = sender.state == .on project?.saveSettings() } @IBAction func firstLineAsTitle(_ sender: NSButton) { guard let project = self.project else { return } project.settings.firstLineAsTitle = sender.state == .on project.saveSettings() let notes = Storage.shared().getNotesBy(project: project) for note in notes { note.invalidateCache() } guard let vc = ViewController.shared() else { return } vc.notesTableView.reloadData() } @IBAction func close(_ sender: Any) { self.dismiss(nil) } @IBAction func showNestedFoldersContent(_ sender: NSButton) { guard let project = self.project else { return } project.settings.showNestedFoldersContent = sender.state == .on project.saveSettings() guard let vc = ViewController.shared() else { return } vc.updateTable() } public func load(project: Project) { self.project = project if project.isVirtual { showInAll.isEnabled = false nestedFoldersContent.isEnabled = false firstLineAsTitle.isEnabled = false } showInAll.state = project.settings.showInCommon ? .on : .off firstLineAsTitle.state = project.settings.isFirstLineAsTitle() ? .on : .off nestedFoldersContent.state = project.settings.showNestedFoldersContent ? .on : .off modificationDate.state = project.settings.sortBy == .modificationDate ? .on : .off creationDate.state = project.settings.sortBy == .creationDate ? .on : .off titleButton.state = project.settings.sortBy == .title ? .on : .off sortByGlobal.state = project.settings.sortBy == .none ? .on : .off directionASC.state = project.settings.sortDirection == .asc ? .on : .off directionDESC.state = project.settings.sortDirection == .desc ? .on : .off if project.parent == nil && !project.isTrash && !project.isEncrypted { gitView.isHidden = false gitViewHeight.constant = 150 } loadGit(project: project) } override func keyDown(with event: NSEvent) { if event.keyCode == kVK_Return || event.keyCode == kVK_Escape { self.dismiss(nil) } } } ================================================ FILE: FSNotes/SidebarScrollView.swift ================================================ // // SidebarScrollView.swift // FSNotes // // Created by Oleksandr Glushchenko on 4/9/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa class SidebarNotesView: NSView { override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) layer?.backgroundColor = NSColor.white.cgColor } } ================================================ FILE: FSNotes/View/AboutImageView.swift ================================================ // // AboutImage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 08.01.2023. // Copyright © 2023 Oleksandr Hlushchenko. All rights reserved. // import Cocoa class AboutImageView: NSImageView { override func mouseEntered(with event: NSEvent) { image = NSImage(named: "friend") } override func mouseExited(with event: NSEvent) { image = NSImage(named: "modern") } override func updateTrackingAreas() { super.updateTrackingAreas() for trackingArea in self.trackingAreas { self.removeTrackingArea(trackingArea) } let options: NSTrackingArea.Options = [.mouseEnteredAndExited, .activeAlways] let trackingArea = NSTrackingArea(rect: self.bounds, options: options, owner: self, userInfo: nil) self.addTrackingArea(trackingArea) } } ================================================ FILE: FSNotes/View/ClickableTextField.swift ================================================ // // ClickableTextField.swift // FSNotes // // Created by Oleksandr Hlushchenko on 13.08.2024. // Copyright © 2024 Oleksandr Hlushchenko. All rights reserved. // import Cocoa class ClickableTextField: NSTextField { override func mouseDown(with event: NSEvent) { super.mouseDown(with: event) guard let vc = ViewController.shared(), let projects = vc.sidebarOutlineView.getSelectedProjects() else { return } vc.getMasterPassword() { password in vc.sidebarOutlineView.unlock(projects: projects, password: password, action: nil) } } } ================================================ FILE: FSNotes/View/EditTextView+Clicked.swift ================================================ // // EditTextView+Clicked.swift // FSNotes // // Created by Oleksandr Hlushchenko on 13.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import Foundation import AppKit extension EditTextView { public func handleEmailLink(_ link: Any) -> Bool { guard let emailString = link as? String, emailString.isValidEmail(), let mailURL = URL(string: "mailto:\(emailString)") else { return false } NSWorkspace.shared.open(mailURL) return true } public func handleAnchorLink(_ link: Any) -> Bool { guard let linkString = link as? String, linkString.startsWith(string: "#") else { return false } let title = String(linkString.dropFirst()).replacingOccurrences(of: "-", with: " ") guard let textRange = textStorage?.string.range(of: "# " + title), let nsRange = textStorage?.string.nsRange(from: textRange) else { return false } setSelectedRange(nsRange) scrollRangeToVisible(nsRange) return true } public func isAttachmentAtPosition(_ charIndex: Int) -> Bool { let range = NSRange(location: charIndex, length: 1) let char = attributedSubstring(forProposedRange: range, actualRange: nil) return char?.attribute(.attachment, at: 0, effectiveRange: nil) != nil } public func handleRegularLink(_ link: Any, at charIndex: Int) -> Bool { guard let url = convertToURL(link) else { super.clicked(onLink: link, at: charIndex) return true } // Handle file:// URLs if url.scheme == "file" { DispatchQueue.main.async { NSWorkspace.shared.activateFileViewerSelecting([url]) } return true } // Handle non-fsnotes URLs with modifiers if url.scheme != "fsnotes" { if let handled = handleURLWithModifiers(url, at: charIndex) { return handled } } super.clicked(onLink: link, at: charIndex) return true } private func convertToURL(_ link: Any) -> URL? { if let url = link as? URL { return url } if let linkString = link as? String { return linkString.createURL(for: self.note) } return nil } private func handleURLWithModifiers(_ url: URL, at charIndex: Int) -> Bool? { guard let event = NSApp.currentEvent else { return nil } // Shift: Open without activation if event.modifierFlags.contains(.shift) { let configuration = NSWorkspace.OpenConfiguration() configuration.activates = false NSWorkspace.shared.open(url, configuration: configuration, completionHandler: nil) return true } // Command: Open normally if event.modifierFlags.contains(.command) { NSWorkspace.shared.open(url) return true } // No modifier: Check user preferences if !UserDefaultsManagement.clickableLinks { setSelectedRange(NSRange(location: charIndex, length: 0)) return true } return nil } } ================================================ FILE: FSNotes/View/EditTextView+Complete.swift ================================================ // // EditTextView+Complete.swift // FSNotes // // Created by Oleksandr Hlushchenko on 06.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import Foundation import AppKit // MARK: - Completion Context enum CompletionContext: Equatable { case wikiLink(startPos: Int) case tag(startPos: Int) case codeBlock(startPos: Int) case none var startPosition: Int? { switch self { case .wikiLink(let pos), .tag(let pos), .codeBlock(let pos): return pos case .none: return nil } } } extension EditTextView { // MARK: - Context Detection func detectCompletionContext() -> CompletionContext { let location = selectedRange().location let text = string as NSString if let codeBlockContext = detectCodeBlockContext(at: location, in: text) { return codeBlockContext } // Disable completion in code blocks guard let ranges = note?.codeBlockRangesCache, !ranges.contains(where: { $0.contains(location) }) else { return .none } if let tagContext = detectTagContext(at: location, in: text) { return tagContext } if let wikiContext = detectWikiContext(at: location, in: text) { return wikiContext } return .none } private func detectCodeBlockContext(at location: Int, in text: NSString) -> CompletionContext? { guard location >= 3 else { return nil } guard let ranges = note?.codeBlockRangesCache, ranges.contains(where: { $0.contains(location) }) || ( // Allow if no code block before !ranges.contains(where: { $0.contains(location - 1) }) && !ranges.contains(where: { $0.contains(location) }) ) else { return nil } let checkRange = NSRange(location: location - 3, length: 3) let lastChars = text.substring(with: checkRange) guard lastChars == "```" else { return detectCodeBlockLanguageInput(at: location, in: text) } let lineStart = findLineStart(at: location - 3, in: text) if lineStart < location - 3 { let beforeRange = NSRange(location: lineStart, length: location - 3 - lineStart) let beforeText = text.substring(with: beforeRange) for char in beforeText { if char != " " && char != "\t" { return nil } } } return .codeBlock(startPos: location) } private func detectCodeBlockLanguageInput(at location: Int, in text: NSString) -> CompletionContext? { var searchPos = location - 1 while searchPos >= 0 && location - searchPos < 30 { if searchPos + 3 <= text.length { let checkRange = NSRange(location: searchPos, length: 3) let chars = text.substring(with: checkRange) if chars == "```" { let lineStart = findLineStart(at: searchPos, in: text) if lineStart < searchPos { let beforeRange = NSRange(location: lineStart, length: searchPos - lineStart) let beforeText = text.substring(with: beforeRange) var isValidStart = true for char in beforeText { if char != " " && char != "\t" { isValidStart = false break } } if !isValidStart { return nil } } let betweenRange = NSRange(location: searchPos + 3, length: location - searchPos - 3) let betweenText = text.substring(with: betweenRange) if !betweenText.contains("\n") { return .codeBlock(startPos: searchPos + 3) } return nil } } searchPos -= 1 } return nil } private func findLineStart(at position: Int, in text: NSString) -> Int { var pos = position - 1 while pos >= 0 { let char = text.substring(with: NSRange(location: pos, length: 1)) if char == "\n" { return pos + 1 } pos -= 1 } return 0 } private func detectTagContext(at location: Int, in text: NSString) -> CompletionContext? { guard UserDefaultsManagement.inlineTags && location >= 1 else { return nil } var searchPos = location - 1 while searchPos >= 0 && location - searchPos < 50 { let char = text.substring(with: NSRange(location: searchPos, length: 1)) if char == "#" { if isValidTagStart(at: searchPos, in: text) { return .tag(startPos: searchPos) } break } else if isWhitespace(char) { break } searchPos -= 1 } return nil } private func detectWikiContext(at location: Int, in text: NSString) -> CompletionContext? { var searchPos = location while searchPos >= 2 && location - searchPos < 100 { let checkRange = NSRange(location: searchPos - 2, length: 2) let chars = text.substring(with: checkRange) if chars == "[[" { let betweenRange = NSRange(location: searchPos, length: location - searchPos) let betweenText = text.substring(with: betweenRange) if !betweenText.contains("]]") { return .wikiLink(startPos: searchPos) } } searchPos -= 1 } return nil } // MARK: - Helpers private func isValidTagStart(at position: Int, in text: NSString) -> Bool { guard position >= 0 else { return false } if position == 0 { return true } let charBefore = text.substring(with: NSRange(location: position - 1, length: 1)) return isWhitespace(charBefore) } private func isWhitespace(_ char: String) -> Bool { return char == " " || char == "\n" || char == "\t" } private func checkForClosingBrackets(at position: Int, in text: NSString) -> Bool { guard position + 2 <= text.length else { return false } let nextRange = NSRange(location: position, length: 2) let nextChars = text.substring(with: nextRange) return nextChars == "]]" } // MARK: - Completion Handlers func handleCompletions(index: UnsafeMutablePointer) -> [String]? { let context = detectCompletionContext() let currentPos = selectedRange().location let text = string as NSString index.pointee = 0 switch context { case .codeBlock(let startPos): return getCodeBlockCompletions(startPos: startPos, currentPos: currentPos, text: text) case .tag(let startPos): return getTagCompletions(startPos: startPos, currentPos: currentPos, text: text) case .wikiLink(let startPos): return getWikiCompletions(startPos: startPos, currentPos: currentPos, text: text) case .none: return nil } } private func getCodeBlockCompletions(startPos: Int, currentPos: Int, text: NSString) -> [String]? { let searchLength = currentPos - startPos let codeLanguages = NotesTextProcessor.getHighlighter().getLanguages() .sorted() if searchLength == 0 { return codeLanguages } let searchRange = NSRange(location: startPos, length: searchLength) let searchText = text.substring(with: searchRange) let filtered = codeLanguages .filter { $0.localizedCaseInsensitiveContains(searchText) } .sorted { a, b in let aStarts = a.range(of: searchText, options: [.caseInsensitive, .anchored]) != nil let bStarts = b.range(of: searchText, options: [.caseInsensitive, .anchored]) != nil if aStarts != bStarts { return aStarts && !bStarts } return a.localizedCaseInsensitiveCompare(b) == .orderedAscending } return filtered.isEmpty ? nil : filtered } private func getTagCompletions(startPos: Int, currentPos: Int, text: NSString) -> [String]? { let searchLength = currentPos - startPos - 1 if searchLength == 0 { if let tags = viewDelegate?.sidebarOutlineView.getAllTags() { return tags.sorted() } } let searchRange = NSRange(location: startPos + 1, length: searchLength) let searchText = text.substring(with: searchRange) if let tags = viewDelegate?.sidebarOutlineView.getAllTags() { let filtered = tags .filter { $0.startsWith(string: searchText) } .sorted() return filtered.isEmpty ? nil : filtered } return nil } private func getWikiCompletions(startPos: Int, currentPos: Int, text: NSString) -> [String]? { let searchLength = currentPos - startPos if searchLength == 0 { let titles = storage.noteList .map { String($0.title) } .filter { !$0.isEmpty } .sorted() return titles } let searchRange = NSRange(location: startPos, length: searchLength) let searchText = text.substring(with: searchRange) if let notes = storage.getBy(contains: searchText) { let titles = notes .map { String($0.title) } .filter { $0.localizedCaseInsensitiveContains(searchText) && !$0.isEmpty && $0 != searchText } .sorted() return titles } return nil } func handleInsertCompletion(word: String, movement: Int, isFinal flag: Bool) { guard flag && movement == NSReturnTextMovement else { return } let context = detectCompletionContext() switch context { case .codeBlock(let startPos): insertCodeBlockCompletion(word, startPos: startPos) case .tag(let startPos): insertTagCompletion(word, startPos: startPos) case .wikiLink(let startPos): insertWikiCompletion(word, startPos: startPos) case .none: break } } private func insertCodeBlockCompletion(_ word: String, startPos: Int) { let currentPos = selectedRange().location let replaceRange = NSRange(location: startPos, length: currentPos - startPos) let nextLineRange = NSRange(location: currentPos + 1, length: 3) let nextLine = (self.string as NSString) .safeSubstring(with: nextLineRange) .trimmingCharacters(in: .whitespacesAndNewlines) let nextLineHasBackticks = (nextLine == "```") var completion = nextLineHasBackticks ? "\(word)\n" : "\(word)\n\n```" // Inside code block without ``` if let ranges = note?.codeBlockRangesCache { for r in ranges where r.contains(startPos) { completion = word break } } suppressCompletion = true if shouldChangeText(in: replaceRange, replacementString: completion) { replaceCharacters(in: replaceRange, with: completion) didChangeText() setSelectedRange(NSRange(location: startPos + word.count + 1, length: 0)) } } private func insertTagCompletion(_ word: String, startPos: Int) { let currentPos = selectedRange().location let replaceRange = NSRange(location: startPos + 1, length: currentPos - startPos - 1) if shouldChangeText(in: replaceRange, replacementString: word) { replaceCharacters(in: replaceRange, with: word) let spacePos = startPos + 1 + word.count if shouldChangeText(in: NSRange(location: spacePos, length: 0), replacementString: " ") { replaceCharacters(in: NSRange(location: spacePos, length: 0), with: " ") } didChangeText() let newPos = startPos + 1 + word.count + 1 setSelectedRange(NSRange(location: newPos, length: 0)) } } private func insertWikiCompletion(_ word: String, startPos: Int) { let text = string as NSString let currentPos = selectedRange().location let hasClosingBrackets = checkForClosingBrackets(at: currentPos, in: text) let replaceRange = NSRange(location: startPos, length: currentPos - startPos) let completion = hasClosingBrackets ? word : "\(word)]]" if shouldChangeText(in: replaceRange, replacementString: completion) { replaceCharacters(in: replaceRange, with: completion) didChangeText() let newPos = hasClosingBrackets ? startPos + word.count + 2 : startPos + completion.count setSelectedRange(NSRange(location: newPos, length: 0)) } } func calculateCompletionRange() -> NSRange { let location = selectedRange().location let context = detectCompletionContext() switch context { case .codeBlock(let startPos): return NSRange(location: startPos, length: location - startPos) case .tag(let startPos): return NSRange(location: startPos + 1, length: location - startPos - 1) case .wikiLink(let startPos): return NSRange(location: startPos, length: location - startPos) case .none: return NSRange(location: location, length: 0) } } } extension NSString { func safeSubstring(with range: NSRange) -> String { let length = self.length if length == 0 { return "" } let safeLocation = max(0, min(range.location, length - 1)) let safeLength = max(0, min(range.length, length - safeLocation)) if safeLength == 0 { return "" } let safeRange = NSRange(location: safeLocation, length: safeLength) return self.substring(with: safeRange) } } ================================================ FILE: FSNotes/View/EditTextView+DragOperation.swift ================================================ // // EditTextView+DragOperation.swift // FSNotes // // Created by Oleksandr Hlushchenko on 15.10.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import Cocoa extension EditTextView { public func handleAttributedText(_ pasteboard: NSPasteboard, note: Note, storage: NSTextStorage, replacementRange: NSRange) -> Bool { let locationDiff = selectedRange().location > replacementRange.location ? replacementRange.location : replacementRange.location - selectedRange().length let insertRange = NSRange(location: locationDiff, length: 0) let removeRange = selectedRange() // drag insertText("", replacementRange: removeRange) guard let data = pasteboard.data(forType: NSPasteboard.attributed), let attributedString = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? NSAttributedString else { return false } // drop insertText(attributedString, replacementRange: insertRange) // select let selectedRange = NSRange(location: locationDiff, length: attributedString.length) setSelectedRange(selectedRange) return true } public func handleNoteReference(_ pasteboard: NSPasteboard, note: Note, replacementRange: NSRange) -> Bool { guard let archivedData = pasteboard.data(forType: NSPasteboard.note), let urls = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, NSURL.self], from: archivedData) as? [URL], let url = urls.first, let draggableNote = Storage.shared().getBy(url: url), let textStorage = self.textStorage else { return false } let title = "[[\(draggableNote.title)]]" DispatchQueue.main.async { self.window?.makeFirstResponder(self) guard let undoManager = self.undoManager else { return } undoManager.beginUndoGrouping() if self.shouldChangeText(in: replacementRange, replacementString: title) { textStorage.replaceCharacters(in: replacementRange, with: title) self.didChangeText() self.setSelectedRange(NSRange(location: replacementRange.location + title.count, length: 0)) } undoManager.endUndoGrouping() undoManager.setActionName("Insert Note Reference") } return true } public func handleURLs(_ pasteboard: NSPasteboard, note: Note, replacementRange: NSRange) -> Bool { guard let urls = pasteboard.readObjects(forClasses: [NSURL.self]) as? [URL], !urls.isEmpty else { return false } note.save(attributed: attributedString()) let group = DispatchGroup() let total = urls.count var results = Array(repeating: nil, count: total) for (index, url) in urls.enumerated() { group.enter() fetchDataFromURL(url: url) { data, error in defer { group.leave() } guard let data = data, error == nil else { return } if url.isWebURL { let title = self.getHTMLTitle(from: data) ?? url.lastPathComponent let text = "[\(title)](\(url.absoluteString))" results[index] = NSAttributedString(string: text) } else if let filePath = ImagesProcessor.writeFile(data: data, url: url, note: note), let fileURL = note.getAttachmentFileUrl( name: filePath.removingPercentEncoding ?? filePath ) { let attributed = NSMutableAttributedString( url: fileURL, title: "", path: filePath ) results[index] = attributed } } } group.notify(queue: .main) { let final = NSMutableAttributedString() for i in 0.. 0 else { return } let selectedRange = selectedRange() let lineRange = textStorage.mutableString.lineRange(for: selectedRange) if lineRange.location == 0 { NSSound.beep() return } let previousLineStart = textStorage.mutableString.lineRange( for: NSRange(location: lineRange.location - 1, length: 0) ).location let previousLineRange = NSRange( location: previousLineStart, length: lineRange.location - previousLineStart ) let currentLinesAttr = textStorage.attributedSubstring(from: lineRange) let previousLineAttr = textStorage.attributedSubstring(from: previousLineRange) let offsetInLine = selectedRange.location - lineRange.location let currentLinesString = currentLinesAttr.string let needsNewline = !currentLinesString.hasSuffix("\n") let newContent = NSMutableAttributedString() newContent.append(currentLinesAttr) if needsNewline { let attrs = currentLinesAttr.length > 0 ? currentLinesAttr.attributes(at: currentLinesAttr.length - 1, effectiveRange: nil) : [:] newContent.append(NSAttributedString(string: "\n", attributes: attrs)) } var previousToAppend = previousLineAttr if needsNewline && previousLineAttr.string.hasSuffix("\n") { let trimmedPrevious = NSMutableAttributedString(attributedString: previousLineAttr) trimmedPrevious.deleteCharacters(in: NSRange(location: trimmedPrevious.length - 1, length: 1)) previousToAppend = trimmedPrevious } newContent.append(previousToAppend) let combinedRange = NSRange( location: previousLineRange.location, length: previousLineRange.length + lineRange.length ) newContent.saveData() if shouldChangeText(in: combinedRange, replacementString: newContent.string) { insertText(newContent, replacementRange: combinedRange) didChangeText() } let newSelectionLocation = previousLineRange.location + offsetInLine setSelectedRange(NSRange( location: newSelectionLocation, length: selectedRange.length )) scrollRangeToVisible(self.selectedRange()) } func moveSelectedLinesDown() { guard let textStorage = textStorage, textStorage.length > 0 else { return } let selectedRange = selectedRange() let lineRange = textStorage.mutableString.lineRange(for: selectedRange) if NSMaxRange(lineRange) >= textStorage.length { NSSound.beep() return } let nextLineRange = textStorage.mutableString.lineRange( for: NSRange(location: NSMaxRange(lineRange), length: 0) ) let currentLinesAttr = textStorage.attributedSubstring(from: lineRange) let nextLineAttr = textStorage.attributedSubstring(from: nextLineRange) let offsetInLine = selectedRange.location - lineRange.location let nextLineString = nextLineAttr.string let needsNewline = !nextLineString.hasSuffix("\n") let newContent = NSMutableAttributedString() var nextLineFinalLength = nextLineAttr.length newContent.append(nextLineAttr) if needsNewline { let attrs = nextLineAttr.length > 0 ? nextLineAttr.attributes(at: nextLineAttr.length - 1, effectiveRange: nil) : [:] newContent.append(NSAttributedString(string: "\n", attributes: attrs)) nextLineFinalLength += 1 } var currentToAppend = currentLinesAttr if needsNewline && currentLinesAttr.string.hasSuffix("\n") { let trimmedCurrent = NSMutableAttributedString(attributedString: currentLinesAttr) trimmedCurrent.deleteCharacters(in: NSRange(location: trimmedCurrent.length - 1, length: 1)) currentToAppend = trimmedCurrent } newContent.append(currentToAppend) let combinedRange = NSRange( location: lineRange.location, length: lineRange.length + nextLineRange.length ) newContent.saveData() if shouldChangeText(in: combinedRange, replacementString: newContent.string) { textStorage.replaceCharacters(in: combinedRange, with: newContent) didChangeText() } let newSelectionLocation = lineRange.location + nextLineFinalLength + offsetInLine setSelectedRange(NSRange( location: newSelectionLocation, length: selectedRange.length )) scrollRangeToVisible(self.selectedRange()) } } ================================================ FILE: FSNotes/View/EditTextView+Todo.swift ================================================ // // EditTextView+Todo.swift // FSNotes // // Created by Oleksandr Hlushchenko on 15.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import Cocoa extension EditTextView { func clearCompletedTodos() { guard let textStorage = textStorage else { return } let fullRange = NSRange(location: 0, length: textStorage.length) let text = textStorage.string as NSString undoManager?.beginUndoGrouping() var linesToRemove: [NSRange] = [] textStorage.enumerateAttribute(.todo, in: fullRange, options: []) { value, range, stop in if let value = value as? Int, value == 1 { let lineRange = text.lineRange(for: range) if !linesToRemove.contains(where: { $0.intersection(lineRange) != nil }) { linesToRemove.append(lineRange) } } } for lineRange in linesToRemove.sorted(by: { $0.location > $1.location }) { if shouldChangeText(in: lineRange, replacementString: "") { textStorage.replaceCharacters(in: lineRange, with: "") didChangeText() } } undoManager?.endUndoGrouping() undoManager?.setActionName("Remove TODO Lines") } } ================================================ FILE: FSNotes/View/EditTextView.swift ================================================ // // EditTextView.swift // FSNotes // // Created by Oleksandr Glushchenko on 8/11/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Cocoa import Carbon.HIToolbox class EditTextView: NSTextView, NSTextFinderClient, NSSharingServicePickerDelegate { public var editorViewController: EditorViewController? public var textStorageProcessor: TextStorageProcessor? public var note: Note? public var viewDelegate: ViewController? let storage = Storage.shared() let caretWidth: CGFloat = 2 var downView: MPreviewView? public var timer: Timer? public var tagsTimer: Timer? public var markdownView: MPreviewContainerView? public var isLastEdited: Bool = false @IBOutlet weak var previewMathJax: NSMenuItem! public var imagesLoaderQueue = OperationQueue.init() public var attributesCachingQueue = OperationQueue.init() private var preview = false public var isScrollPositionSaverLocked = false override func becomeFirstResponder() -> Bool { if let note = self.note { if note.container == .encryptedTextPack { return false } textStorage?.removeHighlight() } loadSelectedRange() return super.becomeFirstResponder() } //MARK: caret width override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) guard UserDefaultsManagement.inlineTags else { return } if #available(OSX 10.16, *) { guard let textStorage = self.textStorage, let layoutManager = self.layoutManager else { return } let fullRange = NSRange(location: 0, length: textStorage.length) attributedString().enumerateAttributes(in: fullRange, options: .reverse) { attributes, range, _ in guard range.location >= 0, range.location + range.length <= textStorage.length else { return } guard attributes.index(forKey: .tag) != nil, let font = attributes[.font] as? NSFont else { return } let tag = attributedString().attributedSubstring(from: range).string let tagAttributes = attributedString().attributes(at: range.location, effectiveRange: nil) let glyphRange = layoutManager.glyphRange(forCharacterRange: range, actualCharacterRange: nil) let ascent = font.ascender let descent = abs(font.descender) let fontHeight = ascent + descent layoutManager.enumerateLineFragments(forGlyphRange: glyphRange) { rect, usedRect, textContainer, lineGlyphRange, stop in let intersectionRange = NSIntersectionRange(glyphRange, lineGlyphRange) guard intersectionRange.length > 0 else { return } var fragmentRect = layoutManager.boundingRect(forGlyphRange: intersectionRange, in: textContainer) fragmentRect.origin.x += self.textContainerOrigin.x fragmentRect.origin.y += self.textContainerOrigin.y fragmentRect = self.convertToLayer(fragmentRect) fragmentRect = fragmentRect.integral let verticalInset = max(0, (fragmentRect.height - fontHeight) / 2) var tagRect = NSRect( x: fragmentRect.minX, y: fragmentRect.minY + verticalInset, width: fragmentRect.width - 3, height: fontHeight ) let oneCharSize = ("A" as NSString).size(withAttributes: tagAttributes) tagRect.size.width += oneCharSize.width * 0.25 tagRect = tagRect.integral NSGraphicsContext.saveGraphicsState() let path = NSBezierPath(roundedRect: tagRect, xRadius: 3, yRadius: 3) NSColor.tagColor.setFill() path.fill() let fragmentCharRange = layoutManager.characterRange(forGlyphRange: intersectionRange, actualGlyphRange: nil) let fragmentText = (tag as NSString).substring(with: NSRange( location: fragmentCharRange.location - range.location, length: fragmentCharRange.length )) var drawAttrs = tagAttributes drawAttrs[.font] = font drawAttrs[.foregroundColor] = NSColor.white drawAttrs.removeValue(forKey: .link) drawAttrs.removeValue(forKey: .baselineOffset) let baselineOrigin = NSPoint(x: tagRect.minX, y: tagRect.minY + descent - 3) (fragmentText as NSString).draw(at: baselineOrigin, withAttributes: drawAttrs) NSGraphicsContext.restoreGraphicsState() } } } } public func initTextStorage() { let processor = TextStorageProcessor() processor.editor = self textStorageProcessor = processor textStorage?.delegate = processor guard let textStorage = self.textStorage, let oldLayoutManager = self.layoutManager, let textContainer = self.textContainer else { return } textStorage.removeLayoutManager(oldLayoutManager) let customLayoutManager = LayoutManager() customLayoutManager.addTextContainer(textContainer) customLayoutManager.delegate = customLayoutManager customLayoutManager.processor = processor textStorage.addLayoutManager(customLayoutManager) } public func configure() { DispatchQueue.main.async { self.updateTextContainerInset() } attributesCachingQueue.qualityOfService = .background textContainerInset.height = 10 isEditable = false let isOpenedWindow = window?.contentViewController as? NoteViewController != nil layoutManager?.allowsNonContiguousLayout = isOpenedWindow ? false : UserDefaultsManagement.nonContiguousLayout layoutManager?.defaultAttachmentScaling = .scaleProportionallyDown let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = CGFloat(UserDefaultsManagement.editorLineSpacing) defaultParagraphStyle = paragraphStyle typingAttributes[.paragraphStyle] = paragraphStyle typingAttributes[.font] = UserDefaultsManagement.noteFont } public func invalidateLayout() { if let length = self.textStorage?.length { self.textStorage?.layoutManagers.first?.invalidateLayout(forCharacterRange: NSRange(location: 0, length: length), actualCharacterRange: nil) } } func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, sharingServicesForItems items: [Any], proposedSharingServices proposedServices: [NSSharingService]) -> [NSSharingService] { return [] } // MARK: Overrides override func drawInsertionPoint(in rect: NSRect, color: NSColor, turnedOn flag: Bool) { var newRect = rect newRect.size.width = caretWidth // Fixes last line height if let textStorage = self.textStorage, let layoutManager = self.layoutManager as? LayoutManager { let insertionPoint = self.selectedRange().location if insertionPoint == textStorage.length, insertionPoint > 0 { let lastIndex = insertionPoint - 1 let attributes = textStorage.attributes(at: lastIndex, effectiveRange: nil) let isNewline: Bool = { let ns = textStorage.string as NSString return ns.character(at: lastIndex) == 0x0A // '\n' }() let fontToUse: NSFont if !isNewline, let font = attributes[.font] as? NSFont { fontToUse = font } else { fontToUse = UserDefaultsManagement.noteFont } newRect.size.height = layoutManager.lineHeight(for: fontToUse) } } let clr = NSColor(red: 0.47, green: 0.53, blue: 0.69, alpha: 1.0) super.drawInsertionPoint(in: newRect, color: clr, turnedOn: flag) } override func updateInsertionPointStateAndRestartTimer(_ restartFlag: Bool) { super.updateInsertionPointStateAndRestartTimer(true) } override func setNeedsDisplay(_ invalidRect: NSRect) { var newInvalidRect = NSRect(origin: invalidRect.origin, size: invalidRect.size) newInvalidRect.size.width += self.caretWidth - 1 super.setNeedsDisplay(newInvalidRect) } override func toggleContinuousSpellChecking(_ sender: Any?) { if let menu = sender as? NSMenuItem { UserDefaultsManagement.continuousSpellChecking = (menu.state == .off) } super.toggleContinuousSpellChecking(sender) } override func toggleGrammarChecking(_ sender: Any?) { if let menu = sender as? NSMenuItem { UserDefaultsManagement.grammarChecking = (menu.state == .off) } super.toggleGrammarChecking(sender) } override func toggleAutomaticSpellingCorrection(_ sender: Any?) { if let menu = sender as? NSMenuItem { UserDefaultsManagement.automaticSpellingCorrection = (menu.state == .off) } super.toggleAutomaticSpellingCorrection(sender) } override func toggleSmartInsertDelete(_ sender: Any?) { if let menu = sender as? NSMenuItem { UserDefaultsManagement.smartInsertDelete = (menu.state == .off) } super.toggleSmartInsertDelete(sender) } override func toggleAutomaticQuoteSubstitution(_ sender: Any?) { if let menu = sender as? NSMenuItem { UserDefaultsManagement.automaticQuoteSubstitution = (menu.state == .off) } super.toggleAutomaticQuoteSubstitution(sender) } override func toggleAutomaticDataDetection(_ sender: Any?) { if let menu = sender as? NSMenuItem { UserDefaultsManagement.automaticDataDetection = (menu.state == .off) } super.toggleAutomaticDataDetection(sender) } override func toggleAutomaticLinkDetection(_ sender: Any?) { if let menu = sender as? NSMenuItem { UserDefaultsManagement.automaticLinkDetection = (menu.state == .off) } super.toggleAutomaticLinkDetection(sender) } override func toggleAutomaticTextReplacement(_ sender: Any?) { if let menu = sender as? NSMenuItem { UserDefaultsManagement.automaticTextReplacement = (menu.state == .off) } super.toggleAutomaticTextReplacement(sender) } override func toggleAutomaticDashSubstitution(_ sender: Any?) { if let menu = sender as? NSMenuItem { UserDefaultsManagement.automaticDashSubstitution = (menu.state == .off) } super.toggleAutomaticDashSubstitution(sender) } private var dragDetected = false override func mouseDown(with event: NSEvent) { guard let note = self.note else { return } guard note.container != .encryptedTextPack else { editorViewController?.unLock(notes: [note]) editorViewController?.vcNonSelectedLabel?.isHidden = false return } if editorViewController?.vcEditor?.isPreviewEnabled() == false { self.isEditable = true } let range = selectedRange if handleTodo(event) { self.window?.makeFirstResponder(self) setSelectedRange(range) self.window?.makeFirstResponder(nil) return } dragDetected = false super.mouseDown(with: event) saveSelectedRange() if !self.dragDetected { self.handleClick(event) self.dragDetected = false } } private func handleTodo(_ event: NSEvent) -> Bool { guard let container = self.textContainer, let manager = self.layoutManager else { return false } let point = self.convert(event.locationInWindow, from: nil) let properPoint = NSPoint(x: point.x - textContainerInset.width, y: point.y) let index = manager.characterIndex(for: properPoint, in: container, fractionOfDistanceBetweenInsertionPoints: nil) let glyphRect = manager.boundingRect(forGlyphRange: NSRange(location: index, length: 1), in: container) guard glyphRect.contains(properPoint) else { return false } if isTodo(index) { guard let f = self.getTextFormatter() else { return false } f.toggleTodo(index) DispatchQueue.main.async { NSCursor.pointingHand.set() } return true } return false } private func handleClick(_ event: NSEvent) { guard let container = self.textContainer, let manager = self.layoutManager else { return } let point = self.convert(event.locationInWindow, from: nil) let properPoint = NSPoint(x: point.x - textContainerInset.width, y: point.y) let index = manager.characterIndex(for: properPoint, in: container, fractionOfDistanceBetweenInsertionPoints: nil) let glyphRect = manager.boundingRect(forGlyphRange: NSRange(location: index, length: 1), in: container) guard glyphRect.contains(properPoint) else { return } if hasAttachment(at: index) { if event.modifierFlags.contains(.command) { openTitleEditor(at: index) } else { openFileViewer(at: index) } return } } private func openTitleEditor(at: Int) { guard let vc = editorViewController, let window = vc.view.window, var attachment = getAttachment(at: at) else { return } vc.alert = NSAlert() let field = NSTextField(frame: NSRect(x: 0, y: 0, width: 290, height: 20)) field.placeholderString = "All Hail the Crimson King" field.stringValue = attachment.title vc.alert?.messageText = NSLocalizedString("Please enter image title:", comment: "Edit area") vc.alert?.accessoryView = field vc.alert?.alertStyle = .informational vc.alert?.addButton(withTitle: "OK") vc.alert?.beginSheetModal(for: window) { (returnCode: NSApplication.ModalResponse) -> Void in if returnCode == NSApplication.ModalResponse.alertFirstButtonReturn { attachment.title = field.stringValue var range = NSRange() if self.textStorage?.attribute(.attachment, at: at, effectiveRange: &range) as? NSTextAttachment != nil { self.textStorage?.addAttribute(.attachmentTitle, value: attachment.title, range: range) let content = NSMutableAttributedString(attributedString: self.attributedString()) _ = self.note?.save(content: content) } } vc.alert = nil } DispatchQueue.main.async { field.becomeFirstResponder() } } private func openFileViewer(at: Int) { guard let attachment = getAttachment(at: at) else { return } let url = attachment.url if !url.isImage { NSWorkspace.shared.activateFileViewerSelecting([url]) return } NSWorkspace.shared.open(url) } override func mouseMoved(with event: NSEvent) { if editorViewController?.vcNonSelectedLabel?.isHidden == false { NSCursor.arrow.set() return } let point = self.convert(event.locationInWindow, from: nil) let properPoint = NSPoint( x: point.x - textContainerInset.width, y: point.y - textContainerInset.height ) guard let container = self.textContainer, let manager = self.layoutManager, let textStorage = self.textStorage else { return } let index = manager.characterIndex(for: properPoint, in: container, fractionOfDistanceBetweenInsertionPoints: nil) guard index < textStorage.length else { return } let glyphRect = manager.boundingRect(forGlyphRange: NSRange(location: index, length: 1), in: container) if glyphRect.contains(properPoint), self.isTodo(index) || self.hasAttachment(at: index) { NSCursor.pointingHand.set() return } if glyphRect.contains(properPoint), let link = textStorage.attribute(.link, at: index, effectiveRange: nil) { if textStorage.attribute(.tag, at: index, effectiveRange: nil) != nil { NSCursor.pointingHand.set() return } if link as? URL != nil { if UserDefaultsManagement.clickableLinks || event.modifierFlags.contains(.command) || event.modifierFlags.contains(.shift) { NSCursor.pointingHand.set() return } NSCursor.iBeam.set() return } } if editorViewController?.vcEditor?.isPreviewEnabled() == true { return } super.mouseMoved(with: event) } public func hasAttachment(at: Int) -> Bool { guard let storage = textStorage, at >= 0, at < storage.length else { return false } guard textStorage?.attribute(.attachment, at: at, effectiveRange: nil) as? NSTextAttachment != nil else { return false } return textStorage?.getMeta(at: at) != nil } public func getAttachment(at: Int) -> (url: URL, title: String, path: String)? { if textStorage?.attribute(.attachment, at: at, effectiveRange: nil) as? NSTextAttachment != nil, let meta = textStorage?.getMeta(at: at) { return meta } return nil } public func isTodo(_ location: Int) -> Bool { guard let storage = self.textStorage else { return false } let range = (storage.string as NSString).paragraphRange(for: NSRange(location: location, length: 0)) let string = storage.attributedSubstring(from: range).string as NSString if storage.attribute(.todo, at: location, effectiveRange: nil) != nil { return true } var length = string.range(of: "- [ ] ").length if length == 0 { length = string.range(of: "- [x] ").length } if length > 0 { let upper = range.location + length if location >= range.location && location <= upper { return true } } return false } override var writablePasteboardTypes: [NSPasteboard.PasteboardType] { get { return [ NSPasteboard.attributed, NSPasteboard.PasteboardType.string, ] } } override var readablePasteboardTypes: [NSPasteboard.PasteboardType] { get { return super.readablePasteboardTypes + [NSPasteboard.attributed] } } override func writeSelection(to pboard: NSPasteboard, type: NSPasteboard.PasteboardType) -> Bool { guard let storage = textStorage else { return false } dragDetected = true let range = selectedRange() let attributedString = NSMutableAttributedString(attributedString: storage.attributedSubstring(from: range)) if type == .string { let plainText = attributedString.unloadAttachments().string pboard.setString(plainText, forType: .string) return true } if type == NSPasteboard.attributed { let attributedString = attributedString.unloadTasks() attributedString.saveData() if let data = try? NSKeyedArchiver.archivedData( withRootObject: attributedString, requiringSecureCoding: false ) { pboard.setData(data, forType: NSPasteboard.attributed) return true } } return false } // Copy empty string override func copy(_ sender: Any?) { let attrString = attributedSubstring(forProposedRange: self.selectedRange, actualRange: nil) if self.selectedRange.length == 1, let url = attrString?.attribute(.attachmentUrl, at: 0, effectiveRange: nil) as? URL { let pb = NSPasteboard.general pb.clearContents() pb.writeObjects([url as NSURL]) return } if selectedRanges.count > 1 { var combined = String() for range in selectedRanges { if let range = range as? NSRange, let sub = attributedSubstring(forProposedRange: range, actualRange: nil) as? NSMutableAttributedString { combined.append(sub.unloadAttachments().string + "\n") } } let pasteboard = NSPasteboard.general pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) pasteboard.setString(combined.trim().removeLastNewLine(), forType: NSPasteboard.PasteboardType.string) return } if self.selectedRange.length == 0, let paragraphRange = self.getParagraphRange(), let paragraph = attributedSubstring(forProposedRange: paragraphRange, actualRange: nil) { let pasteboard = NSPasteboard.general pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) pasteboard.setString(paragraph.string.trim().removeLastNewLine(), forType: NSPasteboard.PasteboardType.string) return } if let menuItem = sender as? NSMenuItem, menuItem.identifier?.rawValue == "copy:", self.selectedRange.length > 0 { let attrString = attributedSubstring(forProposedRange: self.selectedRange, actualRange: nil) if let attrString = attrString, let link = attrString.attribute(.link, at: 0, effectiveRange: nil) as? String { let pasteboard = NSPasteboard.general pasteboard.declareTypes([.string], owner: nil) pasteboard.setString(link, forType: .string) return } } super.copy(sender) } override func paste(_ sender: Any?) { guard let note = self.note else { return } // RTFD if let rtfdData = NSPasteboard.general.data(forType: NSPasteboard.attributed), let attributed = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(rtfdData) as? NSAttributedString { let mutable = NSMutableAttributedString(attributedString: attributed) mutable.loadTasks() breakUndoCoalescing() insertText(mutable, replacementRange: selectedRange()) breakUndoCoalescing() return } // Plain text if let clipboard = NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.string), NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.fileURL) == nil { let attributed = NSMutableAttributedString(string: clipboard.trim()) attributed.loadTasks() breakUndoCoalescing() insertText(attributed, replacementRange: selectedRange()) breakUndoCoalescing() return } if let url = NSURL(from: NSPasteboard.general) { if url.isFileURL && saveFile(url: url as URL, in: note) { return } } // Images png or tiff for type in [NSPasteboard.PasteboardType.png, .tiff] { if let data = NSPasteboard.general.data(forType: type) { guard let attributed = NSMutableAttributedString.build(data: data) else { continue } breakUndoCoalescing() insertText(attributed, replacementRange: selectedRange()) breakUndoCoalescing() return } } super.paste(sender) } override func pasteAsPlainText(_ sender: Any?) { let currentRange = selectedRange() var plainText: String? if let rtfd = NSPasteboard.general.data(forType: NSPasteboard.attributed), let attributedString = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(rtfd) as? NSAttributedString { let mutable = NSMutableAttributedString(attributedString: attributedString) plainText = mutable.unloadAttachments().string } else if let clipboard = NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.string), NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.fileURL) == nil { plainText = clipboard } else if let url = NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.fileURL) { plainText = url } if let plainText = plainText { self.breakUndoCoalescing() self.insertText(plainText, replacementRange: currentRange) self.breakUndoCoalescing() return } return paste(sender) } override func cut(_ sender: Any?) { guard nil != self.note else { super.cut(sender) return } if self.selectedRange.length == 0, let paragraphRange = self.getParagraphRange(), let paragraph = attributedSubstring(forProposedRange: paragraphRange, actualRange: nil) { let pasteboard = NSPasteboard.general pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) pasteboard.setString(paragraph.string.trim().removeLastNewLine(), forType: NSPasteboard.PasteboardType.string) insertText(String(), replacementRange: paragraphRange) return } super.cut(sender) } func getSelectedNote() -> Note? { return ViewController.shared()?.notesTableView?.getSelectedNote() } public func isEditable(note: Note) -> Bool { if note.container == .encryptedTextPack { return false } guard let editor = editorViewController?.vcEditor else { return false } if editor.isPreviewEnabled() { return false } return true } public func getVC() -> EditorViewController { return self.window?.contentViewController as! EditorViewController } public func getEVC() -> EditorViewController? { return self.window?.contentViewController as? EditorViewController } public func save() { guard let note = self.note else { return } note.save(attributed: self.attributedString()) } func fill(note: Note, highlight: Bool = false, force: Bool = false) { isScrollPositionSaverLocked = true if !note.isLoaded { note.load() } viewDelegate?.updateCounters(note: note) textStorage?.setAttributedString(NSAttributedString(string: "")) // Hack for invalidate prev layout data (order is important, only before fill) if let length = textStorage?.length { textStorage?.layoutManagers.first?.invalidateDisplay(forGlyphRange: NSRange(location: 0, length: length)) invalidateLayout() } undoManager?.removeAllActions(withTarget: self) registerHandoff(note: note) // resets timer if editor refilled viewDelegate?.breakUndoTimer.invalidate() unregisterDraggedTypes() registerForDraggedTypes([ NSPasteboard.note, NSPasteboard.PasteboardType.fileURL, NSPasteboard.PasteboardType.URL, NSPasteboard.PasteboardType.string ]) if let label = editorViewController?.vcNonSelectedLabel { label.isHidden = true if note.container == .encryptedTextPack { label.stringValue = NSLocalizedString("Locked", comment: "") label.isHidden = false } else { label.stringValue = NSLocalizedString("None Selected", comment: "") label.isHidden = true } } self.note = note UserDefaultsManagement.lastSelectedURL = note.url editorViewController?.updateTitle(note: note) isEditable = isEditable(note: note) editorViewController?.editorUndoManager = note.undoManager typingAttributes.removeAll() typingAttributes[.font] = UserDefaultsManagement.noteFont if isPreviewEnabled() { loadMarkdownWebView(note: note, force: force) return } markdownView?.removeFromSuperview() markdownView = nil guard let storage = textStorage else { return } if note.isMarkdown(), let content = note.content.mutableCopy() as? NSMutableAttributedString { textStorageProcessor?.detector = CodeBlockDetector() storage.setAttributedString(content) } else { storage.setAttributedString(note.content) } if highlight { textStorage?.highlightKeyword(search: getSearchText()) } viewDelegate?.restoreScrollPosition() } private func loadMarkdownWebView(note: Note, force: Bool) { self.note = nil textStorage?.setAttributedString(NSAttributedString()) self.note = note guard let scrollView = editorViewController?.vcEditorScrollView else { return } if markdownView == nil { let frame = scrollView.bounds let containerView = MPreviewContainerView(frame: frame, note: note, closure: { [weak self] in if let point = self?.note?.contentOffsetWeb { self?.markdownView?.restoreScrollPosition(point) } }) markdownView = containerView containerView.webView.setEditorVC(evc: editorViewController) if self.note == note { scrollView.addSubview(containerView) } } else { /// Resize markdownView let frame = scrollView.bounds markdownView?.frame = frame /// Load note if needed markdownView?.webView.load(note: note, force: force) } } public func lockEncryptedView() { textStorage?.setAttributedString(NSAttributedString()) markdownView?.removeFromSuperview() markdownView = nil isEditable = false if let label = editorViewController?.vcNonSelectedLabel { label.stringValue = NSLocalizedString("Locked", comment: "") label.isHidden = false } } public func clear() { textStorage?.setAttributedString(NSAttributedString()) markdownView?.removeFromSuperview() markdownView = nil isEditable = false window?.title = AppDelegate.appTitle if let label = editorViewController?.vcNonSelectedLabel { label.stringValue = NSLocalizedString("None Selected", comment: "") label.isHidden = false editorViewController?.dropTitle() } self.note = nil if let vc = viewDelegate { vc.updateCounters() } } @IBAction func boldMenu(_ sender: Any) { guard let note = self.note, isEditable else { return } let formatter = TextFormatter(textView: self, note: note) formatter.bold() } @IBAction func italicMenu(_ sender: Any) { guard let note = self.note, isEditable else { return } let formatter = TextFormatter(textView: self, note: note) formatter.italic() } @IBAction func linkMenu(_ sender: Any) { guard let note = self.note, isEditable else { return } let formatter = TextFormatter(textView: self, note: note) formatter.link() } @IBAction func underlineMenu(_ sender: Any) { guard let note = self.note, isEditable else { return } let formatter = TextFormatter(textView: self, note: note) formatter.underline() } @IBAction func strikeMenu(_ sender: Any) { guard let note = self.note, isEditable else { return } let formatter = TextFormatter(textView: self, note: note) formatter.strike() } @IBAction func headerMenu(_ sender: NSMenuItem) { guard let note = self.note, isEditable else { return } guard let id = sender.identifier?.rawValue else { return } let code = Int(id.replacingOccurrences(of: "format.h", with: "")) var string = String() for index in [1, 2, 3, 4, 5, 6] { string = string + "#" if code == index { break } } let formatter = TextFormatter(textView: self, note: note) formatter.header(string) } @IBAction func moveSelectedLinesDown(_ sender: NSMenuItem) { self.moveSelectedLinesDown() } @IBAction func moveSelectedLinesUp(_ sender: NSMenuItem) { self.moveSelectedLinesUp() } @IBAction func clearCompletedTodos(_ sender: NSMenuItem) { self.clearCompletedTodos() } func getParagraphRange() -> NSRange? { guard let storage = textStorage else { return nil } let range = selectedRange() return storage.mutableString.paragraphRange(for: range) } // Clickable links flag changed with cmd / shift override func flagsChanged(with event: NSEvent) { super.flagsChanged(with: event) if let mouseEvent = NSApp.currentEvent { updateCursorForMouse(at: mouseEvent) } } private func updateCursorForMouse(at event: NSEvent) { guard let container = self.textContainer, let manager = self.layoutManager, let textStorage = self.textStorage else { return } let pointInView = self.convert(event.locationInWindow, from: nil) let pointInContainer = NSPoint( x: pointInView.x - textContainerInset.width, y: (self.bounds.size.height - pointInView.y) - textContainerInset.height ) let index = manager.characterIndex( for: pointInContainer, in: container, fractionOfDistanceBetweenInsertionPoints: nil ) guard index < textStorage.length else { NSCursor.iBeam.set() return } if let link = textStorage.attribute(.link, at: index, effectiveRange: nil) { if textStorage.attribute(.tag, at: index, effectiveRange: nil) != nil { NSCursor.pointingHand.set() } else if link as? URL != nil { if UserDefaultsManagement.clickableLinks || NSEvent.modifierFlags.contains(.command) || NSEvent.modifierFlags.contains(.shift) { NSCursor.pointingHand.set() } else { NSCursor.iBeam.set() } } } else { NSCursor.iBeam.set() } } override func keyDown(with event: NSEvent) { defer { saveSelectedRange() } // fixes backtick marked text if let characters = event.characters, characters == "`" { super.insertText("`", replacementRange: selectedRange()) return } guard !( event.modifierFlags.contains(.shift) && [ kVK_UpArrow, kVK_DownArrow, kVK_LeftArrow, kVK_RightArrow ].contains(Int(event.keyCode)) ) else { super.keyDown(with: event) return } guard let note = self.note else { return } // Handle autoclose brackets if UserDefaultsManagement.autocloseBrackets, handleAutocloseBrackets(for: event) { return } // hasMarkedText added for Japanese hack https://yllan.org/blog/archives/231 if event.keyCode == kVK_Tab && !hasMarkedText(){ breakUndoCoalescing() let formatter = TextFormatter(textView: self, note: note) if formatter.isListParagraph() { if NSEvent.modifierFlags.contains(.shift) { formatter.unTab() } else { formatter.tab() } breakUndoCoalescing() return } if UserDefaultsManagement.indentUsing == 0x01 { let tab = TextFormatter.getAttributedCode(string: " ") insertText(tab, replacementRange: selectedRange()) breakUndoCoalescing() return } if UserDefaultsManagement.indentUsing == 0x02 { let tab = TextFormatter.getAttributedCode(string: " ") insertText(tab, replacementRange: selectedRange()) breakUndoCoalescing() return } super.keyDown(with: event) return } if event.keyCode == kVK_Return && !hasMarkedText() && isEditable { breakUndoCoalescing() let formatter = TextFormatter(textView: self, note: note) formatter.newLine() breakUndoCoalescing() return } if event.characters?.unicodeScalars.first == "o" && event.modifierFlags.contains(.command) { guard let storage = textStorage else { return } var location = selectedRange().location if location == storage.length && location > 0 { location = location - 1 } if storage.length > location, let link = textStorage?.attribute(.link, at: location, effectiveRange: nil) as? String { if link.isValidEmail(), let mail = URL(string: "mailto:\(link)") { NSWorkspace.shared.open(mail) } else if let url = URL(string: link) { _ = try? NSWorkspace.shared.open(url, options: .default, configuration: [:]) } } return } super.keyDown(with: event) } // MARK: - Autoclose Brackets private func handleAutocloseBrackets(for event: NSEvent) -> Bool { let brackets: [String: String] = [ "(" : ")", "[" : "]", "{" : "}", "\"" : "\"" ] guard let character = event.characters else { return false } // Check if user is typing a closing bracket let closingBrackets = Array(brackets.values) if closingBrackets.contains(character) { // Check if the next character is the same closing bracket let currentRange = selectedRange() if currentRange.length == 0, let storage = textStorage, currentRange.location < storage.length { let nextCharRange = NSRange(location: currentRange.location, length: 1) let nextCharString = storage.attributedSubstring(from: nextCharRange).string if nextCharString == character { // Skip the closing bracket and move cursor forward setSelectedRange(NSMakeRange(currentRange.location + 1, 0)) return true } } } // Handle opening brackets guard let closingBracket = brackets[character] else { return false } if selectedRange().length > 0 { // Wrap selection with brackets let before = NSMakeRange(selectedRange().lowerBound, 0) self.insertText(character, replacementRange: before) let after = NSMakeRange(selectedRange().upperBound, 0) self.insertText(closingBracket, replacementRange: after) } else { // Insert bracket pair super.keyDown(with: event) self.insertText(closingBracket, replacementRange: selectedRange()) self.moveBackward(self) } return true } override func shouldChangeText(in range: NSRange, replacementString: String?) -> Bool { guard let note = self.note else { return super.shouldChangeText(in: range, replacementString: replacementString) } note.resetAttributesCache() scheduleTagScan(for: note) deleteUnusedImages(checkRange: range) resetTypingAttributes() return super.shouldChangeText(in: range, replacementString: replacementString) } // MARK: Autocomplete overrides var suppressCompletion = false public var forceSystemAutocomplete = false private var isSystemCompletionSession = false override func didChangeText() { super.didChangeText() if suppressCompletion { suppressCompletion = false return } if detectCompletionContext() != .none { complete(nil) } } override func completions(forPartialWordRange charRange: NSRange, indexOfSelectedItem index: UnsafeMutablePointer) -> [String]? { if forceSystemAutocomplete { isSystemCompletionSession = true forceSystemAutocomplete = false return super.completions(forPartialWordRange: charRange, indexOfSelectedItem: index) } return handleCompletions(index: index) } override func insertCompletion(_ word: String, forPartialWordRange charRange: NSRange, movement: Int, isFinal flag: Bool) { if isSystemCompletionSession { super.insertCompletion(word, forPartialWordRange: charRange, movement: movement, isFinal: flag) if flag { isSystemCompletionSession = false } return } handleInsertCompletion(word: word, movement: movement, isFinal: flag) } override var rangeForUserCompletion: NSRange { if isSystemCompletionSession { return super.rangeForUserCompletion } return calculateCompletionRange() } @objc public func scanTagsAndAutoRename() { guard let vc = ViewController.shared() else { return } let notes = vc.tagsScannerQueue attributesCachingQueue.addOperation { for note in notes { note.cache() } } for note in notes { let result = note.scanContentTags() guard let outline = ViewController.shared()?.sidebarOutlineView else { return } let added = result.0 let removed = result.1 if removed.count > 0 { outline.removeTags(removed) } if added.count > 0 { outline.addTags(added) } if let title = note.getAutoRenameTitle() { note.rename(to: title) if let editorViewController = getEVC() { editorViewController.vcTitleLabel?.updateNotesTableView() editorViewController.updateTitle(note: note) } } ViewController.shared()?.tagsScannerQueue.removeAll(where: { $0 === note }) } } func saveSelectedRange() { guard let note = self.note else { return } note.setSelectedRange(range: selectedRange) } func loadSelectedRange() { guard let storage = textStorage else { return } if let range = self.note?.getSelectedRange(), range.upperBound <= storage.length { setSelectedRange(range) scrollToCursor() } } func setEditorTextColor(_ color: NSColor) { if let note = self.note, !note.isMarkdown() { textColor = color } } override func awakeFromNib() { super.awakeFromNib() imagesLoaderQueue.maxConcurrentOperationCount = 3 imagesLoaderQueue.qualityOfService = .userInteractive } override var textContainerOrigin: NSPoint { let origin = super.textContainerOrigin return NSPoint(x: origin.x, y: origin.y - 7) } override func performDragOperation(_ sender: NSDraggingInfo) -> Bool { guard let note = self.note, let storage = textStorage else { return false } let pasteboard = sender.draggingPasteboard let dropPoint = convert(sender.draggingLocation, from: nil) let caretLocation = characterIndexForInsertion(at: dropPoint) let replacementRange = NSRange(location: caretLocation, length: 0) if handleAttributedText(pasteboard, note: note, storage: storage, replacementRange: replacementRange) { return true } if handleNoteReference(pasteboard, note: note, replacementRange: replacementRange) { return true } if handleURLs(pasteboard, note: note, replacementRange: replacementRange) { return true } return super.performDragOperation(sender) } func fetchDataFromURL(url: URL, completion: @escaping (Data?, Error?) -> Void) { let session = URLSession.shared let task = session.dataTask(with: url) { (data, response, error) in if let error = error { completion(nil, error) return } completion(data, nil) } task.resume() } func getHTMLTitle(from data: Data) -> String? { guard let htmlString = String(data: data, encoding: .utf8) else { return nil } return extractTitle(from: htmlString) } func getSearchText() -> String { guard let search = ViewController.shared()?.search else { return String() } if let editor = search.currentEditor(), editor.selectedRange.length > 0 { return (search.stringValue as NSString).substring(with: NSRange(0.. Bool { if let fr = self.window?.firstResponder, fr.isKind(of: EditTextView.self) { return true } return false } @IBAction func shiftLeft(_ sender: Any) { guard let note = self.note, isEditable else { return } let f = TextFormatter(textView: self, note: note) f.unTab() } @IBAction func shiftRight(_ sender: Any) { guard let note = self.note, isEditable else { return } let f = TextFormatter(textView: self, note: note) f.tab() } @IBAction func todo(_ sender: Any) { guard let f = self.getTextFormatter(), isEditable else { return } f.todo() } @IBAction func wikiLinks(_ sender: Any) { guard let note = self.note, isEditable else { return } let formatter = TextFormatter(textView: self, note: note) formatter.wikiLink() } @IBAction func pressBold(_ sender: Any) { guard let note = self.note, isEditable else { return } let formatter = TextFormatter(textView: self, note: note) formatter.bold() } @IBAction func pressItalic(_ sender: Any) { guard let note = self.note, isEditable else { return } let formatter = TextFormatter(textView: self, note: note) formatter.italic() } @IBAction func insertFileOrImage(_ sender: Any) { guard let note = self.note, isEditable else { return } let panel = NSOpenPanel() panel.allowsMultipleSelection = true panel.canChooseDirectories = false panel.canChooseFiles = true panel.canCreateDirectories = true panel.begin { (result) -> Void in if result == NSApplication.ModalResponse.OK { let urls = panel.urls for url in urls { if self.saveFile(url: url, in: note) { if urls.count > 1 { self.insertNewline(nil) } } } if let vc = ViewController.shared() { vc.notesTableView.reloadRow(note: note) } } } } @IBAction func insertCodeBlock(_ sender: NSButton) { guard isEditable else { return } let currentRange = selectedRange() if currentRange.length > 0 { let mutable = NSMutableAttributedString(string: "```\n") if let substring = attributedSubstring(forProposedRange: currentRange, actualRange: nil) { mutable.append(substring) if substring.string.last != "\n" { mutable.append(NSAttributedString(string: "\n")) } } mutable.append(NSAttributedString(string: "```\n")) insertText(mutable, replacementRange: currentRange) setSelectedRange(NSRange(location: currentRange.location + 3, length: 0)) return } insertText("```\n\n```\n", replacementRange: currentRange) setSelectedRange(NSRange(location: currentRange.location + 3, length: 0)) } @IBAction func insertCodeSpan(_ sender: NSMenuItem) { guard isEditable else { return } let currentRange = selectedRange() if currentRange.length > 0 { let mutable = NSMutableAttributedString(string: "`") if let substring = attributedSubstring(forProposedRange: currentRange, actualRange: nil) { mutable.append(substring) } mutable.append(NSAttributedString(string: "`")) insertText(mutable, replacementRange: currentRange) return } insertText("``", replacementRange: currentRange) setSelectedRange(NSRange(location: currentRange.location + 1, length: 0)) } @IBAction func insertList(_ sender: NSMenuItem) { guard let note = self.note, isEditable else { return } let formatter = TextFormatter(textView: self, note: note) formatter.list() } @IBAction func insertOrderedList(_ sender: NSMenuItem) { guard let note = self.note, isEditable else { return } let formatter = TextFormatter(textView: self, note: note) formatter.orderedList() } @IBAction func insertQuote(_ sender: NSMenuItem) { guard let note = self.note, isEditable else { return } let formatter = TextFormatter(textView: self, note: note) formatter.quote() } @IBAction func insertLink(_ sender: Any) { guard let note = self.note, isEditable else { return } let formatter = TextFormatter(textView: self, note: note) formatter.link() } private func getTextFormatter() -> TextFormatter? { guard let note = self.note, isEditable else { return nil } return TextFormatter(textView: self, note: note) } override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool { return true } override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation { if sender.draggingPasteboard.data(forType: NSPasteboard.note) != nil { let dropPoint = convert(sender.draggingLocation, from: nil) let caretLocation = characterIndexForInsertion(at: dropPoint) setSelectedRange(NSRange(location: caretLocation, length: 0)) return .copy } return super.draggingUpdated(sender) } override func clicked(onLink link: Any, at charIndex: Int) { if handleEmailLink(link) { return } if handleAnchorLink(link) { return } if !isAttachmentAtPosition(charIndex) { if handleRegularLink(link, at: charIndex) { return } } } override func viewDidChangeEffectiveAppearance() { UserDataService.instance.isDark = effectiveAppearance.isDark storage.resetCacheAttributes() // clear preview cache MPreviewView.template = nil let webkitPreview = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("wkPreview") try? FileManager.default.removeItem(at: webkitPreview) NotesTextProcessor.hl = nil guard let note = self.note else { return } NotesTextProcessor.highlight(attributedString: note.content) let funcName = effectiveAppearance.isDark ? "switchToDarkMode" : "switchToLightMode" let switchScript = "if (typeof(\(funcName)) == 'function') { \(funcName)(); }" downView?.evaluateJavaScript(switchScript) viewDelegate?.refillEditArea(force: true) } private func saveFile(url: URL, in note: Note) -> Bool { if let data = try? Data(contentsOf: url) { let preferredName = url.lastPathComponent guard let attributed = NSMutableAttributedString.build(data: data, preferredName: preferredName) else { return false } breakUndoCoalescing() insertText(attributed, replacementRange: selectedRange()) breakUndoCoalescing() return true } return false } public func updateTextContainerInset() { textContainerInset.width = getInsetWidth() } public func getInsetWidth() -> CGFloat { let lineWidth = UserDefaultsManagement.lineWidth let margin = UserDefaultsManagement.marginSize let width = frame.width if lineWidth == 1000 { return CGFloat(margin) } guard Float(width) - margin * 2 > lineWidth else { return CGFloat(margin) } return CGFloat((Float(width) - lineWidth) / 2) } private func deleteUnusedImages(checkRange: NSRange) { guard let storage = textStorage, self.note != nil else { return } storage.enumerateAttribute(.attachment, in: checkRange) { (value, range, _) in guard let meta = storage.getMeta(at: range.location) else { return } do { if let data = try? Data(contentsOf: meta.url) { storage.addAttribute(.attachmentSave, value: data, range: range) try FileManager.default.removeItem(at: meta.url) } } catch { print(error) } } } @available(OSX 10.12.2, *) override func makeTouchBar() -> NSTouchBar? { let touchBar = NSTouchBar() touchBar.delegate = self touchBar.defaultItemIdentifiers = [ NSTouchBarItem.Identifier("Todo"), NSTouchBarItem.Identifier("Bold"), NSTouchBarItem.Identifier("Italic"), .fixedSpaceSmall, NSTouchBarItem.Identifier("Link"), NSTouchBarItem.Identifier("Image or file"), NSTouchBarItem.Identifier("CodeBlock"), .fixedSpaceSmall, NSTouchBarItem.Identifier("Indent"), NSTouchBarItem.Identifier("UnIndent") ] return touchBar } @available(OSX 10.12.2, *) override func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? { switch identifier { case NSTouchBarItem.Identifier("Todo"): if let im = NSImage(named: "todo"), im.isValid, im.size.height > 0 { let image = im.tint(color: NSColor.white) image.size = NSSize(width: 20, height: 20) let button = NSButton(image: image, target: self, action: #selector(todo(_:))) button.bezelColor = NSColor(red:0.21, green:0.21, blue:0.21, alpha:1.0) let customViewItem = NSCustomTouchBarItem(identifier: identifier) customViewItem.view = button return customViewItem } case NSTouchBarItem.Identifier("Bold"): if let im = NSImage(named: "bold"), im.isValid, im.size.height > 0 { let image = im.tint(color: NSColor.white) image.size = NSSize(width: 20, height: 20) let button = NSButton(image: image, target: self, action: #selector(pressBold(_:))) button.bezelColor = NSColor(red:0.21, green:0.21, blue:0.21, alpha:1.0) let customViewItem = NSCustomTouchBarItem(identifier: identifier) customViewItem.view = button return customViewItem } case NSTouchBarItem.Identifier("Italic"): if let im = NSImage(named: "italic"), im.isValid, im.size.height > 0 { let image = im.tint(color: NSColor.white) image.size = NSSize(width: 20, height: 20) let button = NSButton(image: image, target: self, action: #selector(pressItalic(_:))) button.bezelColor = NSColor(red:0.21, green:0.21, blue:0.21, alpha:1.0) let customViewItem = NSCustomTouchBarItem(identifier: identifier) customViewItem.view = button return customViewItem } case NSTouchBarItem.Identifier("Image or file"): if let im = NSImage(named: "image"), im.isValid, im.size.height > 0 { let image = im.tint(color: NSColor.white) image.size = NSSize(width: 20, height: 20) let button = NSButton(image: image, target: self, action: #selector(insertFileOrImage(_:))) button.bezelColor = NSColor(red:0.21, green:0.21, blue:0.21, alpha:1.0) let customViewItem = NSCustomTouchBarItem(identifier: identifier) customViewItem.view = button return customViewItem } case NSTouchBarItem.Identifier("Indent"): if let im = NSImage(named: "indent"), im.isValid, im.size.height > 0 { let image = im.tint(color: NSColor.white) image.size = NSSize(width: 20, height: 20) let button = NSButton(image: image, target: self, action: #selector(shiftRight(_:))) button.bezelColor = NSColor(red:0.21, green:0.21, blue:0.21, alpha:1.0) let customViewItem = NSCustomTouchBarItem(identifier: identifier) customViewItem.view = button return customViewItem } case NSTouchBarItem.Identifier("UnIndent"): if let im = NSImage(named: "unindent"), im.isValid, im.size.height > 0 { let image = im.tint(color: NSColor.white) image.size = NSSize(width: 20, height: 20) let button = NSButton(image: image, target: self, action: #selector(shiftLeft(_:))) button.bezelColor = NSColor(red:0.21, green:0.21, blue:0.21, alpha:1.0) let customViewItem = NSCustomTouchBarItem(identifier: identifier) customViewItem.view = button return customViewItem } case NSTouchBarItem.Identifier("CodeBlock"): if let im = NSImage(named: "codeblock"), im.isValid, im.size.height > 0 { let image = im.tint(color: NSColor.white) image.size = NSSize(width: 20, height: 20) let button = NSButton(image: image, target: self, action: #selector(insertCodeBlock(_:))) button.bezelColor = NSColor(red:0.21, green:0.21, blue:0.21, alpha:1.0) let customViewItem = NSCustomTouchBarItem(identifier: identifier) customViewItem.view = button return customViewItem } case NSTouchBarItem.Identifier("Link"): if let im = NSImage(named: "tb_link"), im.isValid, im.size.height > 0 { let image = im.tint(color: NSColor.white) image.size = NSSize(width: 20, height: 20) let button = NSButton(image: image, target: self, action: #selector(insertLink(_:))) button.bezelColor = NSColor(red:0.21, green:0.21, blue:0.21, alpha:1.0) let customViewItem = NSCustomTouchBarItem(identifier: identifier) customViewItem.view = button return customViewItem } default: break } return super.touchBar(touchBar, makeItemForIdentifier: identifier) } override func menu(for event: NSEvent) -> NSMenu? { let menu = super.menu(for: event) let editTitle = NSLocalizedString("Edit Link…", comment: "") if let editLink = menu?.item(withTitle: editTitle) { menu?.removeItem(editLink) } let removeTitle = NSLocalizedString("Remove Link", comment: "") if let removeLink = menu?.item(withTitle: removeTitle) { menu?.removeItem(removeLink) } return menu } /** Handoff methods */ override func updateUserActivityState(_ userActivity: NSUserActivity) { guard let note = self.note else { return } let position = window?.firstResponder == self ? selectedRange().location : -1 let state = editorViewController?.vcEditor?.preview == true ? "preview" : "editor" let data = [ "note-file-name": note.name, "position": String(position), "state": state ] userActivity.addUserInfoEntries(from: data) } override func resignFirstResponder() -> Bool { userActivity?.needsSave = true return super.resignFirstResponder() } public func registerHandoff(note: Note) { self.userActivity?.invalidate() DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { let updateDict: [String: String] = ["note-file-name": note.name] let activity = NSUserActivity(activityType: "es.fsnot.handoff-open-note") activity.isEligibleForHandoff = true activity.userInfo = updateDict activity.title = NSLocalizedString("Open note", comment: "Document opened") self.userActivity = activity self.userActivity?.becomeCurrent() } } public func changePreviewState(_ state: Bool) { preview = state } public func togglePreviewState() { self.preview = !self.preview note?.previewState = self.preview } public func isPreviewEnabled() -> Bool { return preview } public func disablePreviewEditorAndNote() { preview = false note?.previewState = false } public func scheduleTagScan(for note: Note) { if let vc = ViewController.shared(), !vc.tagsScannerQueue.contains(note) { vc.tagsScannerQueue.append(note) } tagsTimer?.invalidate() tagsTimer = Timer.scheduledTimer( timeInterval: 2.5, target: self, selector: #selector(scanTagsAndAutoRename), userInfo: nil, repeats: false ) } public func resetTypingAttributes() { typingAttributes.removeValue(forKey: .attachmentUrl) typingAttributes.removeValue(forKey: .attachmentTitle) typingAttributes.removeValue(forKey: .attachmentPath) typingAttributes.removeValue(forKey: .attachmentSave) typingAttributes.removeValue(forKey: .todo) typingAttributes.removeValue(forKey: .tag) if let style = typingAttributes[.paragraphStyle] as? NSMutableParagraphStyle { style.alignment = .left } typingAttributes[.font] = UserDefaultsManagement.noteFont } } ================================================ FILE: FSNotes/View/EditorScrollView.swift ================================================ // // EditorScrollView.swift // FSNotes // // Created by Oleksandr Glushchenko on 10/7/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa class EditorScrollView: NSScrollView { private var initialHeight: CGFloat? override var isFindBarVisible: Bool { set { // macOS 10.14 margin hack if #available(OSX 10.14, *) { if let clip = self.subviews.first as? NSClipView { clip.contentInsets.top = newValue ? 60 : 10 if newValue, let documentView = self.documentView { documentView.scroll(NSPoint(x: 0, y: -60)) } } } super.isFindBarVisible = newValue } get { return super.isFindBarVisible } } // // // override func findBarViewDidChangeHeight() { // if #available(OSX 10.14, *) { // guard let currentHeight = findBarView?.frame.height else { return } // // guard let initialHeight = self.initialHeight else { // self.initialHeight = currentHeight // return // } // // if let clip = self.subviews.first as? NSClipView { // let margin = currentHeight > initialHeight ? 65 : 40 // clip.contentInsets.top = CGFloat(margin) // // if let documentView = self.documentView { // documentView.scroll(NSPoint(x: 0, y: -margin)) // } // } // } else { // super.findBarViewDidChangeHeight() // } // } } ================================================ FILE: FSNotes/View/EditorSplitView.swift ================================================ // // EditorSplitView.swift // FSNotes // // Created by Oleksandr Glushchenko on 4/20/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa class EditorSplitView: NSSplitView, NSSplitViewDelegate { public var shouldHideDivider = false override func draw(_ dirtyRect: NSRect) { self.delegate = self super.draw(dirtyRect) } override func minPossiblePositionOfDivider(at dividerIndex: Int) -> CGFloat { return 0 } /* func splitView(_ splitView: NSSplitView, constrainMinCoordinate proposedMinimumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { return (shouldHideDivider || UserDefaultsManagement.horizontalOrientation) ? 0 : 200 } func splitView(_ splitView: NSSplitView, constrainMaxCoordinate proposedMaximumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { return UserDefaultsManagement.horizontalOrientation ? 99999 : 350 }*/ override var dividerColor: NSColor { return NSColor.init(named: "divider")! } override var dividerThickness: CGFloat { get { return shouldHideDivider ? 0 : 1 } } func splitViewDidResizeSubviews(_ notification: Notification) { ViewController.shared()?.viewDidResize() } func splitViewWillResizeSubviews(_ notification: Notification) { if let vc = ViewController.shared() { vc.editor.updateTextContainerInset() } } } ================================================ FILE: FSNotes/View/EditorView.swift ================================================ // // EditorView.swift // FSNotes // // Created by Oleksandr Glushchenko on 9/29/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa class EditorView: NSView { override func mouseDown(with event: NSEvent) { guard let vc = ViewController.shared() else { return } vc.editor.mouseDown(with: event) NSApp.mainWindow?.makeFirstResponder(vc.editor) } override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) NSColor(named: "mainBackground")!.setFill() __NSRectFill(dirtyRect) } } ================================================ FILE: FSNotes/View/HyperlinkTextField.swift ================================================ // // HyperlinkTextField.swift // FSNotes // // Created by Oleksandr Hlushchenko on 13.11.2022. // Copyright © 2022 Oleksandr Hlushchenko. All rights reserved. // import Cocoa @IBDesignable class HyperlinkTextField: NSTextField { @IBInspectable var href: String = "" override func awakeFromNib() { super.awakeFromNib() let attributes: [NSAttributedString.Key : Any] = [ NSAttributedString.Key.foregroundColor: NSColor.blue, NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue ] self.attributedStringValue = NSAttributedString(string: self.stringValue, attributes: attributes) } override func mouseDown(with event: NSEvent) { NSWorkspace.shared.open(URL(string: self.href)!) } } ================================================ FILE: FSNotes/View/MPreviewContainerView.swift ================================================ // // MPreviewContainerView.swift // FSNotes // // Created by Oleksandr Hlushchenko on 21.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import AppKit class MPreviewContainerView: NSView { // UI Elements public var webView: MPreviewView! private var findPanel: MPreviewFindPanel! private var findPanelHeightConstraint: NSLayoutConstraint! // Search state private var currentMatchIndex = 0 private var totalMatches = 0 public var isFindPanelVisible = false // MARK: - Initialization init(frame: NSRect, note: Note, closure: MPreviewViewClosure?, force: Bool = false) { super.init(frame: frame) setupWebView(note: note, closure: closure, force: force) setupFindPanel() setupLayout() } required init?(coder: NSCoder) { super.init(coder: coder) } private func setupLayout() { NSLayoutConstraint.activate([ webView.leadingAnchor.constraint(equalTo: leadingAnchor), webView.trailingAnchor.constraint(equalTo: trailingAnchor), webView.topAnchor.constraint(equalTo: findPanel.bottomAnchor), webView.bottomAnchor.constraint(equalTo: bottomAnchor) ]) } private func setupWebView(note: Note, closure: MPreviewViewClosure?, force: Bool) { webView = MPreviewView(frame: bounds, note: note, closure: closure, force: force) webView.translatesAutoresizingMaskIntoConstraints = false addSubview(webView) } private func setupFindPanel() { findPanel = MPreviewFindPanel() findPanel.translatesAutoresizingMaskIntoConstraints = false addSubview(findPanel) NSLayoutConstraint.activate([ findPanel.leadingAnchor.constraint(equalTo: leadingAnchor), findPanel.trailingAnchor.constraint(equalTo: trailingAnchor), findPanel.topAnchor.constraint(equalTo: topAnchor) ]) findPanel.isHidden = true findPanel.panelHeightConstraint.constant = 0 // Callbacks findPanel.onSearch = { [weak self] searchText in self?.performSearch(searchText) } findPanel.onNext = { [weak self] in self?.findNext() } findPanel.onPrevious = { [weak self] in self?.findPrevious() } findPanel.onDone = { [weak self] in self?.hideFindPanel() } } // MARK: - Public API var previewView: MPreviewView { return webView } func showFindPanel() { window?.makeFirstResponder(self) isFindPanelVisible = true let pasteboard = NSPasteboard(name: .find) if let searchText = pasteboard.string(forType: .string) { pasteboard.clearContents() findPanel.searchField.stringValue = searchText findPanel.onSearch?(searchText) } findPanel.show() } func hideFindPanel() { isFindPanelVisible = false findPanel.hide() clearHighlights() } func toggleFindPanel() { if isFindPanelVisible { hideFindPanel() } else { showFindPanel() } } // MARK: - Search Implementation private func performSearch(_ searchText: String) { guard !searchText.isEmpty else { clearHighlights() return } let escapedText = searchText .replacingOccurrences(of: "\\", with: "\\\\") .replacingOccurrences(of: "'", with: "\\'") .replacingOccurrences(of: "\"", with: "\\\"") let jsCode = """ (function() { document.querySelectorAll('.mpreview-find-highlight').forEach(el => { var parent = el.parentNode; parent.replaceChild(document.createTextNode(el.textContent), el); parent.normalize(); }); var searchText = '\(escapedText)'; if (searchText.length === 0) return 0; var escapedSearch = searchText.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'); var regex = new RegExp('(' + escapedSearch + ')', 'gi'); var walker = document.createTreeWalker( document.body, NodeFilter.SHOW_TEXT, { acceptNode: function(node) { if (node.parentNode.nodeName === 'SCRIPT' || node.parentNode.nodeName === 'STYLE' || node.parentNode.classList.contains('mpreview-find-highlight')) { return NodeFilter.FILTER_REJECT; } return NodeFilter.FILTER_ACCEPT; } }, false ); var nodesToReplace = []; while(walker.nextNode()) { var node = walker.currentNode; if(regex.test(node.textContent)) { nodesToReplace.push(node); } } var matchCount = 0; nodesToReplace.forEach(function(node) { var text = node.textContent; var matches = text.match(regex); if (!matches) return; var fragment = document.createDocumentFragment(); var lastIndex = 0; var tempText = text; while(true) { var match = regex.exec(tempText); if (!match) break; var index = match.index; if (index > lastIndex) { fragment.appendChild(document.createTextNode(tempText.substring(lastIndex, index))); } var mark = document.createElement('mark'); mark.className = 'mpreview-find-highlight'; mark.setAttribute('data-index', matchCount); mark.textContent = match[0]; fragment.appendChild(mark); matchCount++; lastIndex = index + match[0].length; } if (lastIndex < text.length) { fragment.appendChild(document.createTextNode(tempText.substring(lastIndex))); } node.parentNode.replaceChild(fragment, node); }); var firstMatch = document.querySelector('.mpreview-find-highlight'); if(firstMatch) { firstMatch.classList.add('current-match'); firstMatch.scrollIntoView({behavior: 'smooth', block: 'center'}); } return matchCount; })(); """ webView.evaluateJavaScript(jsCode) { [weak self] result, error in if error != nil { print("Search error: \\(error)") } if let count = result as? Int { self?.totalMatches = count self?.currentMatchIndex = count > 0 ? 1 : 0 self?.findPanel.updateStatus(current: self?.currentMatchIndex ?? 0, total: self?.totalMatches ?? 0) } } injectHighlightStyles() } func getSelectedText(completion: @escaping (String?) -> Void) { let javascript = "window.getSelection().toString()" webView.evaluateJavaScript(javascript) { (result, error) in if let error = error { print("Error: \(error)") completion(nil) } else { completion(result as? String) } } } private func injectHighlightStyles() { let css = """ mark.mpreview-find-highlight { background-color: rgba(255, 255, 0, 0.35); color: inherit; padding: 1px 0; border-radius: 2px; } mark.mpreview-find-highlight.current-match { background-color: rgba(255, 143, 0, 0.8) !important; outline: 2px solid rgba(255, 100, 0, 0.6); } """ let escapedCSS = css .replacingOccurrences(of: "\n", with: " ") .replacingOccurrences(of: "'", with: "\\'") let jsCode = """ (function() { var style = document.getElementById('mpreview-find-style'); if(!style) { style = document.createElement('style'); style.id = 'mpreview-find-style'; document.head.appendChild(style); } style.innerHTML = '\(escapedCSS)'; })(); """ webView.evaluateJavaScript(jsCode) } public func findNext() { guard totalMatches > 0 else { return } let jsCode = """ (function() { var marks = document.querySelectorAll('.mpreview-find-highlight'); if(marks.length === 0) return 0; var current = document.querySelector('.current-match'); if(current) { current.classList.remove('current-match'); } var currentIndex = current ? Array.from(marks).indexOf(current) : -1; var nextIndex = (currentIndex + 1) % marks.length; marks[nextIndex].classList.add('current-match'); marks[nextIndex].scrollIntoView({behavior: 'smooth', block: 'center'}); return nextIndex + 1; })(); """ webView.evaluateJavaScript(jsCode) { [weak self] result, error in if let index = result as? Int { self?.currentMatchIndex = index self?.findPanel.updateStatus(current: index, total: self?.totalMatches ?? 0) } } } private func findPrevious() { guard totalMatches > 0 else { return } let jsCode = """ (function() { var marks = document.querySelectorAll('.mpreview-find-highlight'); if(marks.length === 0) return 0; var current = document.querySelector('.current-match'); if(current) { current.classList.remove('current-match'); } var currentIndex = current ? Array.from(marks).indexOf(current) : 0; var prevIndex = currentIndex - 1; if(prevIndex < 0) prevIndex = marks.length - 1; marks[prevIndex].classList.add('current-match'); marks[prevIndex].scrollIntoView({behavior: 'smooth', block: 'center'}); return prevIndex + 1; })(); """ webView.evaluateJavaScript(jsCode) { [weak self] result, error in if let index = result as? Int { self?.currentMatchIndex = index self?.findPanel.updateStatus(current: index, total: self?.totalMatches ?? 0) } } } private func clearHighlights() { let jsCode = """ (function() { document.querySelectorAll('.mpreview-find-highlight').forEach(el => { var parent = el.parentNode; parent.replaceChild(document.createTextNode(el.textContent), el); parent.normalize(); }); })(); """ webView.evaluateJavaScript(jsCode) totalMatches = 0 currentMatchIndex = 0 findPanel.updateStatus(current: 0, total: 0) } @objc override func performTextFinderAction(_ sender: Any?) { guard let menuItem = sender as? NSMenuItem else { return } switch NSTextFinder.Action(rawValue: menuItem.tag) { case .showFindInterface: showFindPanel() case .hideFindInterface: hideFindPanel() case .nextMatch: findNext() case .previousMatch: findPrevious() case .showReplaceInterface: break case .replace, .replaceAll, .replaceAndFind: break case .setSearchString: getSelectedText { [weak self] text in if let text = text, !text.isEmpty { self?.performSearch(text) } } case .selectAll, .selectAllInSelection: break default: break } } func getScrollPosition(_ completion: @escaping (CGPoint) -> Void) { let js = "({ x: window.scrollX, y: window.scrollY })" webView.evaluateJavaScript(js) { result, _ in if let dict = result as? [String: CGFloat], let x = dict["x"], let y = dict["y"] { completion(CGPoint(x: x, y: y)) } else { completion(.zero) } } } func restoreScrollPosition(_ point: CGPoint) { let js = "window.scrollTo(\(point.x), \(point.y));" webView.evaluateJavaScript(js, completionHandler: nil) } } ================================================ FILE: FSNotes/View/MPreviewFindPanel.swift ================================================ // // MPreviewFindPanel.swift // FSNotes // // Created by Oleksandr Hlushchenko on 21.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import Cocoa import WebKit final class MPreviewFindPanel: NSVisualEffectView, NSSearchFieldDelegate { // MARK: UI public let searchField = NSSearchField() private let previousButton = NSButton() private let nextButton = NSButton() private let doneButton = NSButton() private let statusLabel = NSTextField() private let containerView = NSView() public var panelHeightConstraint: NSLayoutConstraint! private var containerHeightConstraint: NSLayoutConstraint! // MARK: Callbacks var onSearch: ((String) -> Void)? var onNext: (() -> Void)? var onPrevious: (() -> Void)? var onDone: (() -> Void)? // MARK: Init override init(frame frameRect: NSRect) { super.init(frame: frameRect) setupUI() } required init?(coder: NSCoder) { super.init(coder: coder) setupUI() } // MARK: Setup private func setupUI() { material = .windowBackground blendingMode = .behindWindow state = .active translatesAutoresizingMaskIntoConstraints = false panelHeightConstraint = heightAnchor.constraint(equalToConstant: 36) panelHeightConstraint.isActive = true setupContainer() setupControls() } private func setupContainer() { containerView.translatesAutoresizingMaskIntoConstraints = false addSubview(containerView) containerHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: 28) NSLayoutConstraint.activate([ containerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8), containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8), containerView.centerYAnchor.constraint(equalTo: centerYAnchor), containerHeightConstraint ]) } private func setupControls() { // Search field searchField.placeholderString = NSLocalizedString("Search", comment: "") searchField.delegate = self prepare(searchField) // Buttons configureButton(previousButton, systemImage: "chevron.up", action: #selector(previousClicked)) configureButton(nextButton, systemImage: "chevron.down", action: #selector(nextClicked)) // Status statusLabel.isEditable = false statusLabel.isBordered = false statusLabel.backgroundColor = .clear statusLabel.font = .systemFont(ofSize: 11) statusLabel.textColor = .secondaryLabelColor statusLabel.alignment = .center prepare(statusLabel) // Done doneButton.title = NSLocalizedString("Done", comment: "") doneButton.bezelStyle = .texturedRounded doneButton.target = self doneButton.action = #selector(doneClicked) prepare(doneButton) layoutControls() } private func prepare(_ view: NSView) { view.translatesAutoresizingMaskIntoConstraints = false containerView.addSubview(view) } private func configureButton(_ button: NSButton, systemImage: String, action: Selector) { button.image = NSImage(systemSymbolName: systemImage, accessibilityDescription: nil) button.imagePosition = .imageOnly button.bezelStyle = .texturedRounded button.target = self button.action = action prepare(button) } private func layoutControls() { NSLayoutConstraint.activate([ searchField.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), searchField.centerYAnchor.constraint(equalTo: containerView.centerYAnchor), searchField.widthAnchor.constraint(equalToConstant: 200), previousButton.leadingAnchor.constraint(equalTo: searchField.trailingAnchor, constant: 8), previousButton.centerYAnchor.constraint(equalTo: containerView.centerYAnchor), previousButton.widthAnchor.constraint(equalToConstant: 32), nextButton.leadingAnchor.constraint(equalTo: previousButton.trailingAnchor, constant: 4), nextButton.centerYAnchor.constraint(equalTo: containerView.centerYAnchor), nextButton.widthAnchor.constraint(equalToConstant: 32), statusLabel.leadingAnchor.constraint(equalTo: nextButton.trailingAnchor, constant: 8), statusLabel.centerYAnchor.constraint(equalTo: containerView.centerYAnchor), statusLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: 60), doneButton.leadingAnchor.constraint(equalTo: statusLabel.trailingAnchor, constant: 8), doneButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), doneButton.centerYAnchor.constraint(equalTo: containerView.centerYAnchor) ]) } // MARK: Public API func show() { isHidden = false NSAnimationContext.runAnimationGroup { context in context.duration = 0.25 panelHeightConstraint.animator().constant = 36 alphaValue = 1 } completionHandler: { self.window?.makeFirstResponder(self.searchField) } } func hide() { NSAnimationContext.runAnimationGroup { context in context.duration = 0.25 panelHeightConstraint.animator().constant = 0 alphaValue = 0 } completionHandler: { self.isHidden = true } } func updateStatus(current: Int, total: Int) { statusLabel.stringValue = total > 0 ? "\(current) from \(total)" : "Not found" } func clear() { searchField.stringValue = "" statusLabel.stringValue = "" } // MARK: Actions @objc private func searchFieldChanged(_ sender: NSSearchField) { onSearch?(sender.stringValue) } @objc private func previousClicked() { onPrevious?() } @objc private func nextClicked() { onNext?() } @objc private func doneClicked() { onDone?() } func controlTextDidChange(_ obj: Notification) { if let textField = obj.object as? NSSearchField { onSearch?(textField.stringValue) } } func controlTextDidEndEditing(_ obj: Notification) { let movement = obj.userInfo?["NSTextMovement"] as? Int ?? 0 if movement == NSTextMovement.return.rawValue { onNext?() } } } ================================================ FILE: FSNotes/View/NameTextField.swift ================================================ // // NameTextField.swift // FSNotes // // Created by Oleksandr Glushchenko on 10/9/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa class NameTextField: NSTextField { override func becomeFirstResponder() -> Bool { let status = super.becomeFirstResponder() self.textColor = NSColor.init(named: "mainText") return status } } ================================================ FILE: FSNotes/View/NoteCellView.swift ================================================ // // NoteCellView.swift // FSNotes // // Created by Oleksandr Glushchenko on 7/31/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Cocoa class NoteCellView: NSTableCellView { @IBOutlet var name: NSTextField! @IBOutlet var preview: PreviewTextField! @IBOutlet var date: NSTextField! @IBOutlet var pin: NSImageView! @IBOutlet weak var titleConstraint: NSLayoutConstraint! @IBOutlet weak var imagePreview: NSImageView! @IBOutlet weak var imagePreviewSecond: NSImageView! @IBOutlet weak var imagePreviewThird: NSImageView! public var note: Note? public var contentLength: Int = 0 public var timestamp: Int64? private var previewMaximumLineHeight: CGFloat = 12 private let previewLineSpacing: CGFloat = 3 public var imageKeys = [String]() public var tableView: NotesTableView? { get { guard let vc = ViewController.shared() else { return nil } return vc.notesTableView } } public static var pinImages = [String: NSImage]() public static var pinEncryptedImages = [String: NSImage]() public static var pinSharedImages = [String: NSImage]() override func prepareForReuse() { super.prepareForReuse() imagePreview.image = nil imagePreview.isHidden = true imagePreviewSecond.image = nil imagePreviewSecond.isHidden = true imagePreviewThird.image = nil imagePreviewThird.isHidden = true imageKeys = [] timestamp = nil note = nil name.stringValue = "" preview.stringValue = "" date.stringValue = "" } override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) renderPin() name.layer?.zPosition = 1000 if let descriptor = date.font?.fontDescriptor { date.font = NSFont.init(descriptor: descriptor, size: 11) } date.layer?.cornerRadius = 5 date.layer?.zPosition = 1001 date.isHidden = UserDefaultsManagement.hideDate titleConstraint.constant = UserDefaultsManagement.hideDate ? 0 : 5 if (UserDefaultsManagement.horizontalOrientation) { preview.isHidden = true } else { preview.isHidden = false } if UserDefaultsManagement.hidePreviewImages || UserDefaultsManagement.horizontalOrientation { imagePreview.isHidden = true imagePreviewSecond.isHidden = true imagePreviewThird.isHidden = true } applyPreviewStyle() if !UserDefaultsManagement.horizontalOrientation && !UserDefaultsManagement.hidePreviewImages { self.note?.loadPreviewInfo() } } public func configure(note: Note) { self.note = note } func applyPreviewStyle() { let additionalHeight = CGFloat(UserDefaultsManagement.cellSpacing) guard additionalHeight >= 0 else { applyPreviewAttributes() return } let fontName = UserDefaultsManagement.noteFont.fontName let previewFontSzie = CGFloat(UserDefaultsManagement.previewFontSize) guard let font = NSFont(name: fontName, size: previewFontSzie) else { return } self.previewMaximumLineHeight = font.lineHeightCustom // vertically align var numberOfLines = 0 var frameY = 0 if !UserDefaultsManagement.horizontalOrientation && !UserDefaultsManagement.hidePreview { var size = CGFloat(0) var i = -1 while true { if size > additionalHeight - 8 { break } i += 1 if i == 1 { size += previewMaximumLineHeight } else { size += previewLineSpacing + previewMaximumLineHeight } } numberOfLines = i } if numberOfLines > 1 { frameY = Int( (additionalHeight - previewMaximumLineHeight * CGFloat(numberOfLines) - previewLineSpacing * CGFloat(numberOfLines - 1)) / 2 ) } else { let lines = numberOfLines > 0 ? numberOfLines : 0 frameY = Int( (additionalHeight - previewMaximumLineHeight * CGFloat(lines)) / 2 ) } // save margin if frameY >= 0 { let y = CGFloat(Int(frameY)) adjustTopMargin(margin: y) UserDefaultsManagement.cellViewFrameOriginY = y } // apply font and max lines numbers applyPreviewAttributes(numberOfLines) } func applyPreviewAttributes(_ maximumNumberOfLines: Int = 1) { let string = preview.stringValue let fontName = UserDefaultsManagement.noteFont.fontName let previewFontSize = CGFloat(UserDefaultsManagement.previewFontSize) guard let font = NSFont(name: fontName, size: previewFontSize) else { return } let textParagraph = NSMutableParagraphStyle() textParagraph.lineSpacing = previewLineSpacing textParagraph.maximumLineHeight = previewMaximumLineHeight let attribs = [ NSAttributedString.Key.font: font, NSAttributedString.Key.paragraphStyle: textParagraph ] if maximumNumberOfLines > 0 { preview.attributedStringValue = NSAttributedString.init(string: string, attributes: attribs) preview.maximumNumberOfLines = maximumNumberOfLines } else { preview.attributedStringValue = NSAttributedString() preview.maximumNumberOfLines = -1 } } public func isSelected() -> Bool { if let rowView = self.superview as? NSTableRowView, rowView.isSelected, window?.firstResponder == superview?.superview { return true } return false } public func isAccentColorTint() -> Bool { if let rowView = self.superview as? NSTableRowView, !rowView.isSelected { return true } if let rowView = self.superview as? NSTableRowView, rowView.isSelected, window?.firstResponder == superview?.superview { return false } return true } func renderPin() { if let value = objectValue, let note = value as? Note { if note.isPublished() { if #available(macOS 12.0, *), let image = NSImage(systemSymbolName: "globe", accessibilityDescription: nil) { pin.image = image pin.image?.isTemplate = true pin.contentTintColor = .controlAccentColor } else { pin.image = NSImage(named: "web") pin.image?.isTemplate = true pin.contentTintColor = .controlAccentColor pin.image?.size = NSSize(width: 14, height: 14) } pin.isHidden = false } else if note.isEncrypted() { let systemName = note.isUnlocked() ? "lock.open" : "lock" if let image = NSImage(systemSymbolName: systemName, accessibilityDescription: nil) { pin.image = image pin.image?.isTemplate = true pin.contentTintColor = .controlAccentColor } pin.isHidden = false } else { if #available(macOS 12.0, *), let image = NSImage(systemSymbolName: "pin", accessibilityDescription: nil) { pin.image = image pin.image?.isTemplate = true pin.contentTintColor = .controlAccentColor } else { pin.image = NSImage(named: "pin") pin.image?.isTemplate = true pin.contentTintColor = .controlAccentColor pin.image?.size = NSSize(width: 20, height: 20) } pin.isHidden = !note.isPinned } } } public func styleImageView(imageView: ImageView) { imageView.isHidden = false imageView.layer?.borderWidth = 1 imageView.layer?.borderColor = Color.darkGray.cgColor imageView.layer?.cornerRadius = 4 } public func getPreviewImage(imageUrl: URL, note: Note) -> Image? { let tempURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("MainNotesList") if !FileManager.default.fileExists(atPath: tempURL.path) { try? FileManager.default.createDirectory(at: tempURL, withIntermediateDirectories: false, attributes: nil) } if let cacheName = imageUrl.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)?.md5 { let file = tempURL.appendingPathComponent(cacheName) if FileManager.default.fileExists(atPath: file.path) { if let data = try? Data(contentsOf: file), let image = NSImage(data: data) { return image } } do { let data = try Data(contentsOf: imageUrl) if let image = NSImage(data: data) { let size = CGSize(width: 70, height: 70) if let resized = image.crop(to: size) { let jpegImageData = resized.jpgData try? jpegImageData?.write(to: file, options: .atomic) return resized } } } catch { print(error.localizedDescription) } } return nil } public func adjustPinPosition() { for constraint in self.constraints { if constraint.secondAttribute == .leading, let im = constraint.firstItem as? NSImageView { if im.identifier?.rawValue == "pin" { if let note = objectValue as? Note, !note.showIconInList() { constraint.constant = -25 } else { constraint.constant = 3 } } } } } private func adjustTopMargin(margin: CGFloat) { for constraint in self.constraints { if constraint.secondAttribute == .top, let item = constraint.firstItem { if let firstItem = item as? NSImageView, firstItem.identifier?.rawValue == "pin" { constraint.constant = margin continue } if item.isKind(of: NameTextField.self) { constraint.constant = margin + 1.5 continue } if let item = item as? NSTextField, item.identifier?.rawValue == "cellDate" { constraint.constant = margin + 3.5 } } } } public func fixTopConstraint(position: Int?, note: Note) { guard let tableView = tableView else { return } for constraint in self.constraints { if ["firstImageTop", "secondImageTop", "thirdImageTop"].contains(constraint.identifier) { let ident = constraint.identifier let height = position != nil ? tableView.tableView(tableView, heightOfRow: position!) : self.frame.height self.removeConstraint(constraint) var con = CGFloat(0) if note.getTitle() != nil { con += self.name.frame.height } let isPreviewExist = note.preview.trim().count > 0 if isPreviewExist { con += 3 + self.preview.frame.height } var diff = (height - con - 48) / 2 diff += con var imageLink: NSImageView? switch constraint.identifier { case "firstImageTop": imageLink = self.imagePreview case "secondImageTop": imageLink = self.imagePreviewSecond case "thirdImageTop": imageLink = self.imagePreviewThird default: imageLink = self.imagePreview } guard let firstItem = imageLink else { continue } let secondItem = isPreviewExist ? self.preview : self let secondAttribute: NSLayoutConstraint.Attribute = isPreviewExist ? .bottom : .top let constant = isPreviewExist ? 6 : diff let constr = NSLayoutConstraint(item: firstItem, attribute: .top, relatedBy: .equal, toItem: secondItem, attribute: secondAttribute, multiplier: 1, constant: constant) constr.identifier = ident self.addConstraint(constr) } } } public func attachHeaders(note: Note) { if let title = note.getTitle() { self.name.stringValue = title self.preview.stringValue = note.preview } else { self.name.stringValue = "" self.preview.stringValue = "" } self.date.stringValue = note.getDateForLabel() } } ================================================ FILE: FSNotes/View/NoteRowView.swift ================================================ // // NoteRowView.swift // FSNotes // // Created by Oleksandr Glushchenko on 7/31/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Cocoa class NoteRowView: NSTableRowView { override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) } override func drawSeparator(in dirtyRect: NSRect) { let leftInset: CGFloat = 23 let rightInset: CGFloat = 15 let scale = window?.backingScaleFactor ?? NSScreen.main?.backingScaleFactor ?? 2.0 let pixel = 1.0 / scale let y = floor(bounds.height - pixel) let w = max(0, bounds.width - leftInset - rightInset) NSColor.separatorColor.setFill() NSBezierPath(rect: NSRect(x: leftInset, y: y, width: w, height: pixel)).fill() } } ================================================ FILE: FSNotes/View/NotesCounterView.swift ================================================ // // NotesCounterView.swift // FSNotes // // Created by Oleksandr Hlushchenko on 14.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import Cocoa @IBDesignable class NotesCounterView: NSView { private var visualEffectView: NSVisualEffectView? override init(frame frameRect: NSRect) { super.init(frame: frameRect) setupView() } required init?(coder: NSCoder) { super.init(coder: coder) setupView() } private func setupView() { let effectView = NSVisualEffectView(frame: bounds) effectView.autoresizingMask = [.width, .height] effectView.blendingMode = .behindWindow effectView.material = .contentBackground effectView.state = .followsWindowActiveState addSubview(effectView, positioned: .below, relativeTo: nil) visualEffectView = effectView } } ================================================ FILE: FSNotes/View/NotesTableView.swift ================================================ // // NotesTableView.swift // FSNotes // // Created by Oleksandr Glushchenko on 7/31/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Carbon import Cocoa class NotesTableView: NSTableView, NSTableViewDataSource, NSTableViewDelegate { private var noteList = [Note]() var defaultCell = NoteCellView() var pinnedCell = NoteCellView() var storage = Storage.shared() public var history = [URL]() public var historyPosition = 0 private var selectedHistory: IndexSet? override func draw(_ dirtyRect: NSRect) { allowsTypeSelect = false self.gridColor = NSColor.clear self.dataSource = self self.delegate = self super.draw(dirtyRect) } override func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool { if item.action == #selector(selectAll(_:)) { return numberOfRows > 0 && allowsMultipleSelection } if item.action == #selector(copy(_:)) || item.action == #selector(delete(_:)) || item.action == #selector(forceDeleteNote(_:)) { return selectedRowIndexes.count > 0 } return super.validateUserInterfaceItem(item) } override func keyDown(with event: NSEvent) { guard let vc = self.window?.contentViewController as? ViewController else { super.keyDown(with: event) return } if event.keyCode == kVK_ANSI_N && event.modifierFlags.contains(.control) { vc.noteDown(NSMenuItem()) return } if event.keyCode == kVK_ANSI_P && event.modifierFlags.contains(.control) { vc.noteUp(NSMenuItem()) return } super.keyDown(with: event) } override func keyUp(with event: NSEvent) { guard let vc = self.window?.contentViewController as? ViewController else { super.keyUp(with: event) return } if event.keyCode == kVK_Tab && !event.modifierFlags.contains(.control) { if vc.editor?.isPreviewEnabled() == true { NSApp.mainWindow?.makeFirstResponder(vc.editor.markdownView) } else { vc.focusEditArea() } return } super.keyUp(with: event) } override func mouseDown(with event: NSEvent) { guard let vc = self.window?.contentViewController as? ViewController else { return } let point = convert(event.locationInWindow, from: nil) let row = self.row(at: point) if row >= 0, noteList.indices.contains(row) { let note = noteList[row] if event.modifierFlags.contains(.option) { NSWorkspace.shared.activateFileViewerSelecting([note.url]) return } } if let selectedProject = vc.sidebarOutlineView.getSelectedProject(), selectedProject.isLocked() { vc.sidebarOutlineView.toggleFolderLock(NSMenuItem()) return } UserDataService.instance.searchTrigger = false super.mouseDown(with: event) } func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool { if (noteList.indices.contains(row)) { saveNavigationHistory(note: noteList[row]) } return true } override func rightMouseDown(with event: NSEvent) { UserDataService.instance.searchTrigger = false NSApp.activate(ignoringOtherApps: true) if let window = self.window { window.makeKeyAndOrderFront(nil) } let point = convert(event.locationInWindow, from: nil) let rowIndex = row(at: point) guard rowIndex >= 0, rowIndex < numberOfRows else { return } saveNavigationHistory(note: noteList[rowIndex]) window?.makeFirstResponder(self) if !selectedRowIndexes.contains(rowIndex) { selectRowIndexes(IndexSet(integer: rowIndex), byExtendingSelection: false) scrollRowToVisible(rowIndex) } if rowView(atRow: rowIndex, makeIfNecessary: false) as? NoteRowView != nil, let menu = menu { NSMenu.popUpContextMenu(menu, with: event, for: self) } } @IBAction func delete(_ sender: Any) { guard let vc = ViewController.shared(), let notes = getSelectedNotes() else { return } vc.removeNotes(notes: notes, forceRemove: false, rows: selectedRowIndexes) } @IBAction func forceDeleteNote(_ sender: Any) { guard let vc = ViewController.shared(), let notes = getSelectedNotes() else { return } vc.removeNotes(notes: notes, forceRemove: true, rows: selectedRowIndexes) } public func getNoteList() -> [Note] { return noteList } public func setNoteList(notes: [Note]) { noteList = notes } public func countNotes() -> Int { return noteList.count } public func getIndex(for note: Note) -> Int? { return noteList.firstIndex(where: {$0 === note}) } public func getNote(at index: Int) -> Note? { return noteList.indices.contains(index) ? noteList[index] : nil } // Custom note highlight style func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? { return NoteRowView() } // Populate table data func numberOfRows(in tableView: NSTableView) -> Int { return noteList.count } func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { let height = CGFloat(21 + UserDefaultsManagement.cellSpacing) guard row < noteList.count else { return height } let note = noteList[row] if !note.isLoaded && !note.isLoadedFromCache { note.load() } if !note.isParsed { note.loadPreviewInfo() } if !UserDefaultsManagement.horizontalOrientation && !UserDefaultsManagement.hidePreviewImages, let urls = note.imageUrl, urls.count > 0{ if note.preview.count == 0 { if note.getTitle() != nil { // Title + image return 79 + 17 } // Images only return 79 } // Title + Prevew + Images return height + 58 } // Title + preview return height } // On selected row show notes in right panel func tableViewSelectionDidChange(_ notification: Notification) { selectedHistory = selectedRowIndexes let vc = self.window?.contentViewController as! ViewController defer { vc.updateNotesCounter() } if vc.editAreaScroll.isFindBarVisible { let menu = NSMenuItem(title: "", action: nil, keyEquivalent: "") menu.tag = NSTextFinder.Action.hideFindInterface.rawValue vc.editor.performTextFinderAction(menu) } if UserDataService.instance.isNotesTableEscape { if vc.sidebarOutlineView.selectedRow == -1 { UserDataService.instance.isNotesTableEscape = false } vc.sidebarOutlineView.deselectAll(nil) vc.sidebarOutlineView.reloadTags() vc.editor.clear() return } // Select row if (noteList.indices.contains(selectedRow)) { let note = noteList[selectedRow] guard selectedRowIndexes.count == 0x01 else { vc.editor.clear() return } vc.editor.changePreviewState(note.previewState) vc.editor.fill(note: note, highlight: true) if UserDefaultsManagement.focusInEditorOnNoteSelect && !UserDataService.instance.searchTrigger { vc.focusEditArea() } return } // Clean vc.editor.clear() if !UserDefaultsManagement.inlineTags { vc.sidebarOutlineView.deselectAllTags() } } func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? { if (noteList.indices.contains(row)) { return noteList[row] } return nil } func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool { var urls = [URL]() var contentUrls = [URL]() for row in rowIndexes { let note = noteList[row] urls.append(note.url) if let url = note.getContentFileURL() { contentUrls.append(url) } } pboard.clearContents() pboard.writeObjects(contentUrls as [NSPasteboardWriting]) if let data = try? NSKeyedArchiver.archivedData(withRootObject: urls, requiringSecureCoding: true) { pboard.setData(data, forType: NSPasteboard.note) } return true } @IBAction func copy(_ sender: Any) { guard let vc = ViewController.shared() else { return } vc.saveTextAtClipboard() } func getNoteFromSelectedRow() -> Note? { var note: Note? = nil let selected = self.selectedRow if (selected < 0) { return nil } if (noteList.indices.contains(selected)) { note = noteList[selected] } return note } func getSelectedNote() -> Note? { var note: Note? = nil let row = selectedRow if (noteList.indices.contains(row)) { note = noteList[row] } return note } func getSelectedNotes() -> [Note]? { var notes = [Note]() for row in selectedRowIndexes { if (noteList.indices.contains(row)) { notes.append(noteList[row]) } } if notes.isEmpty { return nil } return notes } public func deselectNotes() { self.deselectAll(nil) } override func performKeyEquivalent(with event: NSEvent) -> Bool { if event.modifierFlags.contains(.control) && event.keyCode == kVK_Tab { return true } return super.performKeyEquivalent(with: event) } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { guard noteList.indices.contains(row) else { return nil } let note = noteList[row] if (note.isPinned) { pinnedCell = makeCell(note: note) pinnedCell.pin.frame.size.width = 23 return pinnedCell } defaultCell = makeCell(note: note) defaultCell.pin.frame.size.width = 0 return defaultCell } func tableView(_ tableView: NSTableView, rowActionsForRow row: Int, edge: NSTableView.RowActionEdge) -> [NSTableViewRowAction] { guard edge == .trailing else { return [] } guard noteList.indices.contains(row) else { return [] } let deleteAction = NSTableViewRowAction(style: .destructive, title: NSLocalizedString("Delete", comment: "")) { [weak self] (action, row) in guard let self = self else { return } guard self.noteList.indices.contains(row) else { return } let noteToDelete = self.noteList[row] if let vc = self.window?.contentViewController as? ViewController { vc.removeNotes(notes: [noteToDelete]) } } deleteAction.backgroundColor = .systemRed return [deleteAction] } func makeCell(note: Note) -> NoteCellView { let cell = makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "NoteCellView"), owner: self) as! NoteCellView cell.imageKeys = [] cell.timestamp = nil cell.imagePreview.image = nil cell.imagePreview.isHidden = true cell.imagePreviewSecond.image = nil cell.imagePreviewSecond.isHidden = true cell.imagePreviewThird.image = nil cell.imagePreviewThird.isHidden = true cell.configure(note: note) cell.loadImagesPreview() cell.attachHeaders(note: note) return cell } override func willOpenMenu(_ menu: NSMenu, with event: NSEvent) { guard let vc = ViewController.shared() else { return } if clickedRow > -1 { selectRowIndexes([clickedRow], byExtendingSelection: false) } if selectedRow < 0 { return } menu.autoenablesItems = false for menuItem in menu.items { if vc.processFileMenuItems(menuItem, menuId: "popup") { menuItem.isEnabled = true } else { menuItem.isEnabled = vc.processShareMenuItems(menuItem, menuId: "popup") } } vc.loadMoveMenu() } public func selectCurrent() { guard noteList.count > 0 else { return } UserDataService.instance.searchTrigger = false let i = selectedRowIndexes.count > 0 ? selectedRowIndexes : [0] if let first = i.first { saveNavigationHistory(note: noteList[first]) selectRowIndexes(i, byExtendingSelection: false) scrollRowToVisible(first) } } public func selectNext() { UserDataService.instance.searchTrigger = false let i = selectedRow + 1 guard noteList.indices.contains(i) else { return } saveNavigationHistory(note: noteList[i]) selectRowIndexes([i], byExtendingSelection: false) scrollRowToVisible(i) } public func selectPrev() { UserDataService.instance.searchTrigger = false let i = selectedRow - 1 guard noteList.indices.contains(i) else { return } saveNavigationHistory(note: noteList[i]) selectRowIndexes([i], byExtendingSelection: false) scrollRowToVisible(i) } public func selectRow(_ i: Int) { if (noteList.indices.contains(i)) { DispatchQueue.main.async { self.selectRowIndexes([i], byExtendingSelection: false) self.scrollRowToVisible(i) } } } public func selectRowAndSidebarItem(note: Note) { guard let vc = ViewController.shared() else { return } if let index = getIndex(for: note) { selectRow(index) } else { vc.sidebarOutlineView.select(note: note) } } func setSelected(note: Note) { if let i = getIndex(for: note) { selectRow(i) scrollRowToVisible(i) } } public func select(note: Note) { if let i = getIndex(for: note) { if noteList.indices.contains(i) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.selectRowIndexes([i], byExtendingSelection: false) self.scrollRowToVisible(i) } } } } public func removeRows(notes: [Note]) { guard let vc = ViewController.shared() else { return } var indexSet = IndexSet() for note in notes { if let i = noteList.firstIndex(where: {$0 === note}) { indexSet.insert(i) } } guard !indexSet.isEmpty else { return } beginUpdates() for i in indexSet.sorted().reversed() { noteList.remove(at: i) } removeRows(at: indexSet, withAnimation: .slideDown) endUpdates() if UserDefaultsManagement.inlineTags { vc.sidebarOutlineView.removeTags(notes: notes) } } public func insertRows(notes: [Note]) { guard let vc = self.window?.contentViewController as? ViewController else { return } var insert = [Note]() for note in notes { if noteList.first(where: { $0.isEqualURL(url: note.url) }) == nil, vc.storage.searchQuery.isFit(note: note) { insert.append(note) } } guard !insert.isEmpty else { return } beginUpdates() noteList.append(contentsOf: insert) self.noteList = vc.storage.sortNotes(noteList: self.noteList) var indexSet = IndexSet() for note in insert { if let noteIndex = self.noteList.firstIndex(of: note) { indexSet.insert(noteIndex) } } self.insertRows(at: indexSet, withAnimation: .effectFade) endUpdates() for note in insert { vc.sidebarOutlineView.insertTags(note: note) } } private func reloadRows(notes: [Note]) { for note in notes { note.invalidateCache() note.loadPreviewInfo() self.performReload(note: note) } } @objc public func unDelete(_ urls: [URL: URL]) { guard let vc = ViewController.shared() else { return } var invertedMapping: [URL: URL] = [:] for (src, dst) in urls { do { if let note = storage.getBy(url: src) { storage.removeBy(note: note) if let destination = Storage.shared().getProjectByNote(url: dst) { note.moveImages(to: destination) } } try FileManager.default.moveItem(at: src, to: dst) invertedMapping[dst] = src } catch { print(error) } } // Register redo (delete again) if let md = AppDelegate.mainWindowController, !invertedMapping.isEmpty { let undoManager = md.notesListUndoManager undoManager.registerUndo(withTarget: self) { notesTableView in let restoredNotes = invertedMapping.keys.compactMap { url in vc.storage.getBy(url: url) } if !restoredNotes.isEmpty { vc.removeNotes(notes: restoredNotes, forceRemove: false, rows: nil) } } undoManager.setActionName(NSLocalizedString("Delete", comment: "")) } } public func countVisiblePinned() -> Int { var i = 0 for note in noteList { if (note.isPinned) { i += 1 } } return i } public func reloadRow(note: Note) { DispatchQueue.global(qos: .userInitiated).async { note.invalidateCache() note.loadPreviewInfo() DispatchQueue.main.async { self.performReload(note: note) } } } private func performReload(note: Note) { guard let i = self.noteList.firstIndex(of: note) else { return } let urls = note.imageUrl if let cell = self.view(atColumn: 0, row: i, makeIfNecessary: false) as? NoteCellView { cell.date.stringValue = note.getDateForLabel() cell.loadImagesPreview(position: i, urls: urls) cell.attachHeaders(note: note) cell.renderPin() cell.applyPreviewStyle() self.noteHeightOfRows(withIndexesChanged: [i]) } } public func reloadDate(note: Note) { DispatchQueue.main.async { if self.numberOfRows > 0, let i = self.noteList.firstIndex(of: note) { if let cell = self.view(atColumn: 0, row: i, makeIfNecessary: false) as? NoteCellView { cell.date.stringValue = note.getDateForLabel() } } } } public func saveNavigationHistory(note: Note) { guard history.last != note.url else { historyPosition = history.count - 1 return } history.append(note.url) if history.count > 100 { history.removeFirst() } else { historyPosition = history.count - 1 } } public func enableLockedProject() { ViewController.shared()?.lockedFolder.isHidden = false clean() } public func disableLockedProject() { ViewController.shared()?.lockedFolder.isHidden = true } public func clean() { noteList.removeAll() reloadData() } public func doVisualChanges(results: ([Note], [Note], [Note])) { guard results.0.count > 0 || results.1.count > 0 || results.2.count > 0 else { return } DispatchQueue.main.async { if let vc = ViewController.shared(), vc.splitView.subviews[0].frame.width > 10 { self.removeRows(notes: results.0) self.insertRows(notes: results.1) self.reloadRows(notes: results.2) } } } } ================================================ FILE: FSNotes/View/OutlineHeaderView.swift ================================================ // // OutlineHeaderView.swift // FSNotes // // Created by Oleksandr Glushchenko on 7/21/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa class OutlineHeaderView: NSView { override func mouseDown(with event: NSEvent) { if event.clickCount == 2 { if let md = AppDelegate.mainWindowController, let actionOnDoubleClick = UserDefaults.standard.object(forKey: "AppleActionOnDoubleClick") as? String { switch actionOnDoubleClick { case "Maximize": md.maximizeWindow() case "Minimize": md.window?.performMiniaturize(nil) default: break } } } else { super.mouseDown(with: event) } } override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) let lightColor = NSColor(red:1.00, green:1.00, blue:1.00, alpha:1.0) let darkColor = NSColor(red:0.16, green:0.17, blue:0.18, alpha:1.0) if NSAppearance.current.isDark { darkColor.setFill() } else { lightColor.setFill() } dirtyRect.fill() } override var mouseDownCanMoveWindow: Bool { return true } } ================================================ FILE: FSNotes/View/PreviewTextField.swift ================================================ // // PreviewTextField.swift // FSNotes // // Created by Oleksandr Glushchenko on 10/28/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Cocoa class PreviewTextField: NSTextField { override var intrinsicContentSize: NSSize { if maximumNumberOfLines == -1 { let width = super.intrinsicContentSize.width return NSSize(width: width, height: 0) } return super.intrinsicContentSize } override var textColor: NSColor? { set { super.textColor = newValue if attributedStringValue.length > 0 { var attributes = attributedStringValue.attributes(at: 0, effectiveRange: nil) attributes[.foregroundColor] = newValue attributedStringValue = NSAttributedString.init(string: self.stringValue, attributes: attributes) } } get { return super.textColor } } } ================================================ FILE: FSNotes/View/SearchTextField.swift ================================================ // // SearchTextField.swift // FSNotes // // Created by Oleksandr Glushchenko on 8/3/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Cocoa import Carbon.HIToolbox class SearchTextField: NSSearchField, NSSearchFieldDelegate { public var vcDelegate: ViewController! private var filterQueue = OperationQueue.init() public var searchQuery = "" public var selectedRange = NSRange() public var skipAutocomplete = false public var timestamp: Int64? private var lastQueryLength: Int = 0 private var lastQuery = String() public var lastSearchQuery = String() public var searchesMenu: NSMenu? = nil public func generateRecentMenu() -> NSMenu { let recentsTitle = NSLocalizedString("Recents", comment: "") let menu = NSMenu(title: recentsTitle) menu.autoenablesItems = true if let recent = UserDefaultsManagement.recentSearches, recent.count > 0 { let recentsSearchTitle = NSLocalizedString("Recents Search", comment: "") menu.addItem(withTitle: recentsSearchTitle, action: nil, keyEquivalent: "") var i = 1 for title in recent { let menuItem = NSMenuItem(title: title, action: #selector(selectRecent(_:)), keyEquivalent: String(i)) menuItem.target = self menu.addItem(menuItem) i += 1 } menu.addItem(NSMenuItem.separator()) let clearTitle = NSLocalizedString("Clear", comment: "") let menuItem = NSMenuItem(title: clearTitle, action: #selector(cleanRecents(_:)), keyEquivalent: "d") menuItem.target = self menu.addItem(menuItem) return menu } menu.addItem(withTitle: "No Recent Search", action: nil, keyEquivalent: "") return menu } override func textDidEndEditing(_ notification: Notification) { self.skipAutocomplete = false self.lastQuery = String() self.lastQueryLength = 0 addRecent(query: stringValue) } override func keyUp(with event: NSEvent) { if (event.keyCode == kVK_DownArrow) { vcDelegate.focusTable() vcDelegate.notesTableView.selectCurrent() return } if (event.keyCode == kVK_LeftArrow && stringValue.count == 0) { vcDelegate.sidebarOutlineView.window?.makeFirstResponder(vcDelegate.sidebarOutlineView) let index = vcDelegate.sidebarOutlineView.selectedRowIndexes.count > 0 ? vcDelegate.sidebarOutlineView.selectedRowIndexes : [0] vcDelegate.sidebarOutlineView.selectRowIndexes(index, byExtendingSelection: false) return } if event.keyCode == kVK_Delete || event.keyCode == kVK_ForwardDelete { self.skipAutocomplete = true return } } func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { switch commandSelector.description { case "moveDown:": if let editor = currentEditor() { let text = editor.string let location = editor.selectedRange.location let length = editor.selectedRange.length if length > 0 && location > 0 && location <= text.count { let endIndex = text.index(text.startIndex, offsetBy: location, limitedBy: text.endIndex) ?? text.endIndex let query = String(text[.. 0 { self.stringValue = query } } else { if text.count > 0 { self.stringValue = text } } } addRecent(query: stringValue) return true case "cancelOperation:": self.skipAutocomplete = true self.lastQuery = String() self.filterQueue.cancelAllOperations() return true case "deleteBackward:": self.skipAutocomplete = true self.lastQuery = String() self.filterQueue.cancelAllOperations() textView.deleteBackward(self) return true case "insertNewline:", "insertNewlineIgnoringFieldEditor:": if let note = vcDelegate.editor.getSelectedNote(), stringValue.utf16.count > 0, note.title.lowercased() == stringValue.lowercased() || note.fileName.lowercased() == stringValue.lowercased() { if note.title.lowercased() == stringValue.lowercased() && note.title != stringValue { stringValue = note.title } if note.fileName.lowercased() == stringValue.lowercased() && note.fileName != stringValue { stringValue = note.fileName } markCompleteonAsSuccess() if vcDelegate.vcEditor?.isPreviewEnabled() == true && vcDelegate.editor.note?.container != .encryptedTextPack { vcDelegate.vcEditor?.disablePreviewEditorAndNote() DispatchQueue.main.async { self.vcDelegate.refillEditArea() NSApp.mainWindow?.makeFirstResponder(self.vcDelegate.editor) } } else { DispatchQueue.main.async { self.vcDelegate.focusEditArea() } } } else { vcDelegate.makeNote(self) } addRecent(query: stringValue) return true case "insertTab:": markCompleteonAsSuccess() if vcDelegate.vcEditor?.isPreviewEnabled() == true { NSApp.mainWindow?.makeFirstResponder(vcDelegate.editor.markdownView) } else { vcDelegate.focusEditArea() } vcDelegate.editor.scrollToCursor() return true case "deleteWordBackward:": self.skipAutocomplete = true self.lastQuery = String() self.filterQueue.cancelAllOperations() textView.deleteWordBackward(self) lastQueryLength = self.stringValue.utf16.count return true case "noop:": if let event = NSApp.currentEvent, event.modifierFlags.contains(.command) && event.keyCode == kVK_Return { vcDelegate.makeNote(self) return true } return false default: return false } } func controlTextDidChange(_ obj: Notification) { search() // Clean as lastSearchQuery used by highlighter if stringValue.count == 0 { lastSearchQuery = String() } } public func suggestAutocomplete(_ note: Note, filter: String) { guard note.title.lowercased() != filter.lowercased(), let editor = currentEditor() else { return } if note.title.lowercased().starts(with: filter.lowercased()) { if note.title.lowercased() != stringValue.lowercased() { stringValue = filter + String(note.title.utf16.suffix(note.title.utf16.count - filter.utf16.count))! lastQuery = stringValue lastQueryLength = stringValue.utf16.count } editor.selectedRange = NSRange(filter.utf16.count.. self.lastQueryLength { self.skipAutocomplete = false } self.lastQueryLength = searchText.count if let query = getSearchTextExceptCompletion() { self.lastSearchQuery = query } self.filterQueue.cancelAllOperations() self.filterQueue.addOperation { self.vcDelegate.updateTable() { if let note = self.vcDelegate.notesTableView.getNoteList().first { DispatchQueue.main.async() { if let searchQuery = self.getSearchTextExceptCompletion() { if self.lastSearchQuery != searchQuery { return } self.savePasteboard(value: searchQuery) let search = searchQuery.lowercased() if note.title.lowercased() == search || UserDefaultsManagement.textMatchAutoSelection { self.vcDelegate.notesTableView.setSelected(note: note) self.stringValue = searchQuery return } else if !self.skipAutocomplete && (note.title.lowercased().starts(with: search) || note.fileName.lowercased().starts(with: search)) { self.vcDelegate.notesTableView.setSelected(note: note) self.suggestAutocomplete(note, filter: searchQuery) return } else { self.vcDelegate.editor.clear() } } } } else { DispatchQueue.main.async { self.vcDelegate.editor.clear() } } } } } // Used in NSTextFinder cmd-f | cmd-g private func savePasteboard(value: String) { let pb = NSPasteboard(name: NSPasteboard.Name.find) pb.declareTypes([.textFinderOptions, .string], owner: nil) pb.setString(value, forType: NSPasteboard.PasteboardType.string) } private func getSearchTextExceptCompletion() -> String? { guard let editor = currentEditor() else { return nil } if editor.selectedRange.location > 0 { return String(editor.string.prefix(editor.selectedRange.location)) } return nil } private func markCompleteonAsSuccess() { currentEditor()?.selectedRange = NSRange(location: stringValue.count, length: 0) self.skipAutocomplete = false self.lastQuery = String() self.lastQueryLength = 0 } @IBAction public func selectRecent(_ sender: NSMenuItem) { stringValue = sender.title search() } @IBAction public func cleanRecents(_ sender: NSMenuItem) { UserDefaultsManagement.recentSearches = nil searchesMenu = generateRecentMenu() } public func addRecent(query: String) { let query = query.trim() guard query.trim().count > 0 else { return } var recents = UserDefaultsManagement.recentSearches ?? [String]() if recents.contains(query) { if let index = recents.firstIndex(of: query) { recents.remove(at: index) } } recents.insert(query, at: 0) if recents.count > 9 { recents = recents.dropLast() } UserDefaultsManagement.recentSearches = recents searchesMenu = generateRecentMenu() } } ================================================ FILE: FSNotes/View/SidebarCellView.swift ================================================ // // SidebarCellView.swift // FSNotes // // Created by Oleksandr Glushchenko on 4/7/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa class SidebarCellView: NSTableCellView { @IBOutlet weak var icon: NSImageView! @IBOutlet weak var label: NSTextField! public var type: SidebarItemType? public var storage = Storage.shared() @IBAction func projectName(_ sender: NSTextField) { let cell = sender.superview as? SidebarCellView guard let project = cell?.objectValue as? Project else { return } let src = project.url let dst = project.url.deletingLastPathComponent().appendingPathComponent(sender.stringValue, isDirectory: true) do { if FileManager.default.fileExists(atPath: dst.path) { sender.stringValue = project.url.lastPathComponent return } try FileManager.default.moveItem(at: src, to: dst) } catch { sender.stringValue = project.url.lastPathComponent let alert = NSAlert() alert.messageText = error.localizedDescription alert.runModal() } } } ================================================ FILE: FSNotes/View/SidebarHeaderCellView.swift ================================================ // // SidebarHeaderCellView.swift // FSNotes // // Created by Олександр Глущенко on 15.10.2019. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Cocoa class SidebarHeaderCellView: NSTableCellView { @IBOutlet weak var label: NSTextField! @IBOutlet weak var icon: NSImageView! } ================================================ FILE: FSNotes/View/SidebarNotesView.swift ================================================ // // SidebarNotesView.swift // FSNotes // // Created by Oleksandr Glushchenko on 4/9/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa class SidebarNotesView: NSView { // override func draw(_ dirtyRect: NSRect) { // super.draw(dirtyRect) // // if UserDefaultsManagement.appearanceType != AppearanceType.Custom, #available(OSX 10.13, *) { // NSColor(named: "mainBackground")!.setFill() // __NSRectFill(dirtyRect) // } else { // layer?.backgroundColor = NSColor.white.cgColor // } // } } ================================================ FILE: FSNotes/View/SidebarOutlineView.swift ================================================ // // SidebarProjectView.swift // FSNotes // // Created by Oleksandr Glushchenko on 4/9/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa import Foundation import Carbon.HIToolbox class SidebarOutlineView: NSOutlineView, NSOutlineViewDelegate, NSOutlineViewDataSource { public var sidebarItems: [Any]? = nil public var viewDelegate: ViewController? = nil public var storage = Storage.shared() public var isFirstLaunch = true public var selectNote: Note? = nil private var selectedSidebarItems: [SidebarItem]? private var selectedProjects: [Project]? private var selectedTags: [String]? private var cellView: SidebarCellView? // MARK: Override override func rightMouseDown(with event: NSEvent) { guard let vc = ViewController.shared() else { return } let point = convert(event.locationInWindow, from: nil) let rowIndex = row(at: point) if (rowIndex < 0 || self.numberOfRows < rowIndex) { return } if let item = item(atRow: rowIndex) as? SidebarItem { if item.type == .Separator { return } } if !selectedRowIndexes.contains(rowIndex) { selectRowIndexes(IndexSet(integer: rowIndex), byExtendingSelection: false) scrollRowToVisible(rowIndex) } if rowView(atRow: rowIndex, makeIfNecessary: false) as? SidebarTableRowView != nil { window?.makeFirstResponder(self) if let menu = menu { menu.autoenablesItems = false for item in menu.items { item.isEnabled = vc.processLibraryMenuItems(item, menuId: "folderPopup") } NSMenu.popUpContextMenu(menu, with: event, for: self) } } } override func draw(_ dirtyRect: NSRect) { allowsTypeSelect = false delegate = self dataSource = self registerForDraggedTypes([ NSPasteboard.PasteboardType(kUTTypeFileURL as String), NSPasteboard.note, NSPasteboard.project ]) super.draw(dirtyRect) } override func keyDown(with event: NSEvent) { // Tab to search if event.keyCode == kVK_Tab { self.viewDelegate?.search.becomeFirstResponder() return } // Focus on note list if event.keyCode == kVK_RightArrow { if let fr = NSApp.mainWindow?.firstResponder, let vc = self.viewDelegate, fr.isKind(of: SidebarOutlineView.self) { if let tag = item(atRow: selectedRow) as? FSTag, tag.isExpandable(), !isItemExpanded(tag) { super.keyDown(with: event) return } if let project = item(atRow: selectedRow) as? Project, project.isExpandable(), !isItemExpanded(project) { super.keyDown(with: event) return } if let project = item(atRow: selectedRow) as? Project, project.isLocked() { toggleFolderLock(NSMenuItem()) return } vc.notesTableView.selectCurrent() NSApp.mainWindow?.makeFirstResponder(vc.notesTableView) return } } super.keyDown(with: event) } override func expandItem(_ item: Any?, expandChildren: Bool) { if let project = item as? Project { project.isExpanded = true } super.expandItem(item, expandChildren: expandChildren) storage.saveProjectsExpandState() } override func collapseItem(_ item: Any?, collapseChildren: Bool) { if let project = item as? Project { project.isExpanded = false } super.collapseItem(item, collapseChildren: collapseChildren) storage.saveProjectsExpandState() } override func selectRowIndexes(_ indexes: IndexSet, byExtendingSelection extend: Bool) { guard let index = indexes.first else { return } var extend = extend if (item(atRow: index) as? FSTag) != nil { for i in selectedRowIndexes { if nil != item(atRow: i) as? FSTag { deselectRow(i) } } extend = true } super.selectRowIndexes(indexes, byExtendingSelection: extend) } // MARK: Delegates func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool { guard let vc = ViewController.shared() else { return false } guard let sidebarItems = self.sidebarItems else { return false } // Drag and drop project (reorder) if let data = info.draggingPasteboard.string(forType: NSPasteboard.project) { let url = URL(fileURLWithPath: data) guard let project = Storage.shared().getProjectBy(url: url) else { return false } // Get src index for child and root folders var srcIndex: Int? let dstProject = item as? Project if dstProject != nil, let srcParent = project.parent, !srcParent.isDefault { srcIndex = srcParent.child.firstIndex(where: { $0 === project }) } else { srcIndex = sidebarItems.firstIndex(where: { $0 as? Project === project }) } guard let srcIndex = srcIndex else { return false } var diff = 0 if srcIndex > index { diff = 0 } else { diff = -1 } outlineView.moveItem(at: srcIndex, inParent: item, to: index + diff, inParent: item) if item == nil { self.sidebarItems?.remove(at: srcIndex) self.sidebarItems?.insert(project, at: index + diff) // Save order if let si = self.sidebarItems { var toSave = [Project]() for sidebarItem in si { // Save all projects from this level if let siProject = sidebarItem as? Project, project.parent === siProject.parent || (project.isBookmark && siProject.parent?.isDefault == true) || (project.parent?.isDefault == true && siProject.isBookmark) { toSave.append(siProject) } } saveOrderFor(projects: toSave) } } else { project.parent?.child.remove(at: srcIndex) project.parent?.child.insert(project, at: index + diff) // Save order if let projects = project.parent?.child { saveOrderFor(projects: projects) } } return true } // Drag and drop Note let board = info.draggingPasteboard var urls = [URL]() if let data = info.draggingPasteboard.data(forType: NSPasteboard.note), let unarchivedData = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, NSURL.self], from: data) as? [URL] { urls = unarchivedData } // tags if let tag = item as? FSTag { if urls.count > 0, Storage.shared().getBy(url: urls.first!) != nil { for url in urls { if let note = Storage.shared().getBy(url: url) { note.addTag(tag.getFullName()) _ = note.scanContentTags() viewDelegate?.notesTableView.reloadRow(note: note) if viewDelegate?.editor.note == note { viewDelegate?.refillEditArea(force: true) } } } } return true } // projects var maybeProject: Project? if let sidebarItem = item as? SidebarItem, let sidebarProject = sidebarItem.project { maybeProject = sidebarProject } if let sidebarProject = item as? Project { maybeProject = sidebarProject } if let sidebarItem = item as? SidebarItem, sidebarItem.type == .Inbox { maybeProject = Storage.shared().getDefault() } guard let project = maybeProject else { return false } if urls.count > 0, Storage.shared().getBy(url: urls.first!) != nil { var notes = [Note]() for url in urls { if let note = Storage.shared().getBy(url: url) { notes.append(note) } } if project.isTrash { vc.editor.clear() vc.storage.removeNotes(notes: notes) { _ in DispatchQueue.main.async { vc.notesTableView.removeRows(notes: notes) } } } else { vc.moveReq(notes: notes, project: project) { success in guard success else { return } } } return true } guard let draggedURLs = board.readObjects(forClasses: [NSURL.self], options: nil) as? [URL] else { return false } for url in draggedURLs { var isDirectory = ObjCBool(true) if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory), isDirectory.boolValue && !url.path.contains(".textbundle") { let dirName = url.lastPathComponent let dirDst = project.url.appendingPathComponent(dirName) if !FileManager.default.fileExists(atPath: dirDst.path) { try? FileManager.default.copyItem(at: url, to: dirDst) } else { let alert = NSAlert() alert.alertStyle = .critical let information = NSLocalizedString("Folder with name '%@' already exist", comment: "") alert.informativeText = String(format: information, dirName) alert.runModal() } } else { _ = vc.copy(project: project, url: url) } } return true } func outlineView(_ outlineView: NSOutlineView, pasteboardWriterForItem item: Any) -> NSPasteboardWriting? { guard let project = item as? Project, getSidebarTags() == nil else { return nil } let item = NSPasteboardItem() item.setString(project.url.path, forType: NSPasteboard.project) return item } func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation { if let archivedData = info.draggingPasteboard.string(forType: NSPasteboard.project) { let url = URL(fileURLWithPath: archivedData) guard let project = Storage.shared().getProjectBy(url: url) else { return NSDragOperation() } let dstProject = item as? Project if isAllowedDropIndex(srcProject: project, dstProject: dstProject, dstIndex: index) { return .move } return NSDragOperation() } let board = info.draggingPasteboard var isLocalNote = false var urls = [URL]() if let archivedData = info.draggingPasteboard.data(forType: NSPasteboard.note), let urlsUnarchived = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, NSURL.self], from: archivedData) as? [URL] { urls = urlsUnarchived if let url = urls.first, Storage.shared().getBy(url: url) != nil { isLocalNote = true } // Disable drag and drop notes between sidebar items if index > -1 { return NSDragOperation(rawValue: 0) } } if item as? Project != nil || (item as? SidebarItem)?.project != nil { return isLocalNote ? .move : .copy } if item as? FSTag != nil { return .copy } guard let sidebarItem = item as? SidebarItem else { return NSDragOperation() } switch sidebarItem.type { case .Inbox: return .move case .Trash: if isLocalNote { return .move } break case .Separator: guard sidebarItem.isSelectable() else { break } if isLocalNote { return .move } if let urls = board.readObjects(forClasses: [NSURL.self], options: nil) as? [URL], urls.count > 0 { return .copy } break default: break } return NSDragOperation() } func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int { if let tag = item as? FSTag { return tag.child.count } if let project = item as? Project { return project.child.count } if let sidebar = sidebarItems, item == nil { return sidebar.count } return 0 } func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat { if let si = item as? SidebarItem { if si.type == .Separator { return 15 } if si.type == .Header { return 50 } } return 25 } func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool { if let tag = item as? FSTag { return tag.isExpandable() } if let project = item as? Project { return project.isExpandable() } return false } func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { if let tag = item as? FSTag { return tag.child[index] } if let project = item as? Project { return project.child[index] } if let sidebar = sidebarItems, item == nil { return sidebar[index] } return String() } func outlineView(_ outlineView: NSOutlineView, objectValueFor tableColumn: NSTableColumn?, byItem item: Any?) -> Any? { return item } func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { let cell = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "DataCell"), owner: self) as! SidebarCellView cell.icon.contentTintColor = NSColor.controlAccentColor if let tag = item as? FSTag { cell.type = .Tag let image = NSImage(named: "sidebar_tag") image?.isTemplate = true cell.icon.image = image cell.icon.isHidden = false cell.label.frame.origin.x = 25 cell.textField?.stringValue = tag.getName() } else if let project = item as? Project { if project.isEncrypted { if project.isLocked() { cell.type = .ProjectEncryptedLocked let image = NSImage(named: "sidebar_project_encrypted_locked") image?.isTemplate = true cell.icon.image = image } else { cell.type = .ProjectEncryptedUnlocked let image = NSImage(named: "sidebar_project_encrypted_unlocked") image?.isTemplate = true cell.icon.image = image } } else { cell.type = .Project let image = NSImage(named: "sidebar_project") image?.isTemplate = true cell.icon.image = image } cell.icon.isHidden = false cell.label.frame.origin.x = 25 cell.textField?.stringValue = project.label } else if let si = item as? SidebarItem { let name = si.type == .Separator ? "" : si.name cell.textField?.stringValue = name cell.type = si.type if let name = si.type.icon, let image = si.getIcon(name: name) { cell.icon.image = image } else { cell.icon.image = nil } cell.icon.isHidden = false cell.label.frame.origin.x = 25 if si.type == .Header { let cell = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "HeaderCell"), owner: self) as! SidebarHeaderCellView cell.label.frame.origin.x = 2 cell.label.stringValue = name return cell } } return cell } func outlineView(_ outlineView: NSOutlineView, isGroupItem item: Any) -> Bool { return false } func outlineView(_ outlineView: NSOutlineView, shouldSelectItem item: Any) -> Bool { if nil != item as? FSTag { return true } if nil != item as? Project { return true } if let sidebarItem = item as? SidebarItem { return sidebarItem.isSelectable() } return false } func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? { return SidebarTableRowView(frame: NSZeroRect) } func outlineViewSelectionDidChange(_ notification: Notification) { defer { isFirstLaunch = false } if Storage.shared().welcomeProject != nil { Storage.shared().welcomeProject = nil return } guard let vd = viewDelegate else { return } guard let view = notification.object as? NSOutlineView else { return } viewDelegate?.notesTableView.disableLockedProject() if UserDataService.instance.isNotesTableEscape { UserDataService.instance.isNotesTableEscape = false } let hasChangedSidebarItemsState = isChangedSidebarItemsState() let hasChangedProjectsState = isChangedProjectsState() let hasChangedTagsState = isChangedTagsState() if hasChangedTagsState || hasChangedProjectsState || hasChangedSidebarItemsState { vd.editor.clear() } let i = view.selectedRow if UserDefaultsManagement.inlineTags, view.item(atRow: i) as? FSTag == nil, hasChangedProjectsState || hasChangedSidebarItemsState { reloadTags() } if let item = view.item(atRow: i) as? SidebarItem { if UserDefaultsManagement.lastSidebarItem == item.type.rawValue && !hasChangedTagsState && !isFirstLaunch { return } UserDefaultsManagement.lastSidebarItem = item.type.rawValue UserDefaultsManagement.lastProjectURL = nil } if let selectedProject = view.item(atRow: i) as? Project { if UserDefaultsManagement.lastProjectURL == selectedProject.url && !hasChangedTagsState && !isFirstLaunch { return } UserDefaultsManagement.lastProjectURL = selectedProject.url UserDefaultsManagement.lastSidebarItem = nil if selectedProject.isLocked() { viewDelegate?.notesTableView.enableLockedProject() } } if !isFirstLaunch { vd.search.stringValue = "" } guard !UserDataService.instance.skipSidebarSelection else { UserDataService.instance.skipSidebarSelection = false return } vd.buildSearchQuery() vd.updateTable() { if let note = self.selectNote { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { if let i = vd.notesTableView.getIndex(for: note) { vd.notesTableView.selectRowIndexes([i], byExtendingSelection: false) vd.notesTableView.scrollRowToVisible(i) } } self.selectNote = nil } } } // MARK: Actions @IBAction func revealInFinder(_ sender: Any) { if getSidebarItems()?.first?.type == .Inbox { if let url = Storage.shared().getDefault()?.url { NSWorkspace.shared.activateFileViewerSelecting([url]) } return } guard let projects = getSelectedProjects() else { return } let urls = projects.map { $0.url } if urls.count > 0 { NSWorkspace.shared.activateFileViewerSelecting(urls) } } @IBAction func renameFolderMenu(_ sender: Any) { guard let vc = ViewController.shared(), let sidebarOutlineView = vc.sidebarOutlineView else { return } if sidebarOutlineView.getSidebarTags() != nil { sidebarOutlineView.renameTag(NSMenuItem()) return } guard sidebarOutlineView.getSelectedProject() != nil else { return } guard let projectRow = sidebarOutlineView.rowView(atRow: sidebarOutlineView.selectedRow, makeIfNecessary: false), let cell = projectRow.view(atColumn: 0) as? SidebarCellView else { return } cell.label.isEditable = true cell.label.becomeFirstResponder() } @IBAction public func removeTags(_ sender: NSMenuItem) { guard let vc = ViewController.shared() else { return } guard let window = MainWindowController.shared() else { return } guard let selectedTags = vc.sidebarOutlineView.getSidebarTags() else { return } let alert = NSAlert() vc.alert = alert let messageText = NSLocalizedString("Are you really want to remove %d tag(s)? This action can not be undone.", comment: "") alert.messageText = NSLocalizedString("Remove Tags", comment: "") alert.informativeText = String(format: messageText, selectedTags.count) alert.alertStyle = .informational alert.addButton(withTitle: NSLocalizedString("Remove", comment: "")) alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) alert.beginSheetModal(for: window) { (returnCode: NSApplication.ModalResponse) -> Void in if returnCode == NSApplication.ModalResponse.alertFirstButtonReturn { let notes = vc.notesTableView.getNoteList() var plainTags = [String]() for index in vc.sidebarOutlineView.selectedRowIndexes { if let tag = vc.sidebarOutlineView.item(atRow: index) as? FSTag { plainTags.append(contentsOf: tag.getAllChild()) } } vc.sidebarOutlineView.remove(tags: plainTags, from: notes) } NSApp.mainWindow?.makeFirstResponder(vc.sidebarOutlineView) vc.alert = nil } } @IBAction func renameTag(_ sender: NSMenuItem) { guard let vc = ViewController.shared() else { return } guard let window = MainWindowController.shared() else { return } guard let tags = vc.sidebarOutlineView.getRawSidebarTags() else { return } let alert = NSAlert() vc.alert = alert let field = NSTextField(frame: NSRect(x: 0, y: 0, width: 290, height: 20)) if let name = tags.first?.getFullName() { field.stringValue = name } alert.messageText = NSLocalizedString("Rename Tags", comment: "") alert.informativeText = NSLocalizedString("Please enter tag name:", comment: "") alert.accessoryView = field alert.alertStyle = .informational alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) alert.beginSheetModal(for: window) { (returnCode: NSApplication.ModalResponse) -> Void in if returnCode == NSApplication.ModalResponse.alertFirstButtonReturn { let name = field.stringValue.replacingOccurrences(of: "\\s", with: "", options: NSString.CompareOptions.regularExpression, range: nil) self.rename(tags: tags, name: name) } NSApp.mainWindow?.makeFirstResponder(vc.sidebarOutlineView) vc.alert = nil } field.becomeFirstResponder() } @IBAction func delete(_ sender: Any) { guard let vc = ViewController.shared(), let sidebarOutlineView = vc.sidebarOutlineView else { return } if sidebarOutlineView.getSidebarTags() != nil { sidebarOutlineView.removeTags(NSMenuItem()) return } guard let projects = sidebarOutlineView.getSelectedProjects() else { return } for project in projects { delete(project: project) } UserDefaultsManagement.lastSidebarItem = nil UserDefaultsManagement.lastProjectURL = nil UserDefaultsManagement.lastSelectedURL = nil } private func delete(project: Project) { guard let vc = ViewController.shared() else { return } if !(project.isDefault || project.isBookmark) { guard let window = MainWindowController.shared() else { return } let alert = NSAlert.init() vc.alert = alert let messageText = NSLocalizedString("Are you sure you want to remove project \"%@\" and all files inside?", comment: "") alert.messageText = String(format: messageText, project.label) alert.informativeText = NSLocalizedString("This action cannot be undone.", comment: "Delete menu") alert.addButton(withTitle: NSLocalizedString("Remove", comment: "Delete menu")) alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Delete menu")) alert.beginSheetModal(for: window) { (returnCode: NSApplication.ModalResponse) -> Void in if returnCode == NSApplication.ModalResponse.alertFirstButtonReturn { do { if let note = vc.editor.note { vc.closeAllOpenedWindows(where: note) if note.project == project { vc.editor.clear() } } self.removeRows(projects: [project]) try FileManager.default.removeItem(at: project.url) self.storage.cleanCachedTree(url: project.url) } catch { print(error) } NSApp.mainWindow?.makeFirstResponder(vc.sidebarOutlineView) } vc.alert = nil } return } let projects = storage.getAvailableProjects().filter({ $0.url.path.starts(with: project.url.path) }) for item in projects { SandboxBookmark().removeBy(item.url) } vc.sidebarOutlineView.removeRows(projects: projects) vc.sidebarOutlineView.selectRowIndexes([0], byExtendingSelection: false) vc.updateTable() } @IBAction func removeFolderEncryption(_ sender: NSMenuItem) { guard let vc = ViewController.shared(), let projects = vc.sidebarOutlineView.getSelectedProjects() else { return } guard let firstProject = projects.first else { return } if firstProject.isEncrypted { vc.getMasterPassword() { password in vc.sidebarOutlineView.decrypt(projects: projects, password: password) } } } @IBAction func toggleFolderLock(_ sender: NSMenuItem) { guard let vc = ViewController.shared(), let projects = vc.sidebarOutlineView.getSelectedProjects() else { return } guard let firstProject = projects.first else { return } // Encrypt if !firstProject.isEncrypted { vc.getMasterPassword(forEncrypt: true) { password in vc.sidebarOutlineView.encrypt(projects: projects, password: password) } return } // Lock password exist if firstProject.password != nil { vc.sidebarOutlineView.lock(projects: projects) // Unlock } else { let action = sender.identifier?.rawValue vc.getMasterPassword() { password in vc.sidebarOutlineView.unlock(projects: projects, password: password, action: action) } } } public func decrypt(projects: [Project], password: String) { var decryptedQty = 0 var total = 0 for project in projects { let notes = project.storage.getNotesBy(project: project) total += notes.count let decrypted = project.decrypt(password: password) decryptedQty = decrypted.count self.showTags(notes: decrypted) } DispatchQueue.main.async { guard decryptedQty > 0 || total == 0 else { self.wrongPassAlert() return } guard let vc = ViewController.shared() else { return } vc.notesTableView.disableLockedProject() vc.notesTableView.reloadData() vc.updateTable() self.reloadData(forRowIndexes: self.selectedRowIndexes, columnIndexes: [0]) } } public func encrypt(projects: [Project], password: String) { for project in projects { let encrypted = project.encrypt(password: password) self.hideTags(notes: encrypted) } DispatchQueue.main.async { guard let vc = ViewController.shared() else { return } vc.notesTableView.enableLockedProject() self.reloadData(forRowIndexes: self.selectedRowIndexes, columnIndexes: [0]) // Lock all editors let editors = AppDelegate.getEditTextViews() for editor in editors { if let evc = editor.editorViewController { evc.refillEditArea() } } } } public func lock(projects: [Project]) { guard let vc = ViewController.shared() else { return } var locked = [Note]() for project in projects { locked.append(contentsOf: project.lock()) } hideTags(notes: locked) if let selectedProject = getSelectedProject(), projects.contains(selectedProject) { vc.notesTableView.enableLockedProject() vc.updateTable() vc.editor.clear() } for project in projects { reloadItem(project) } // Lock all editors let editors = AppDelegate.getEditTextViews() for editor in editors { if let evc = editor.editorViewController { evc.refillEditArea() } } } public func unlock(projects: [Project], password: String, action: String? = nil) { var unlocked = [Note]() var isEmptyDir = false for project in projects { let result = project.unlock(password: password) // no notes if result.0.count == 0 { isEmptyDir = true continue } unlocked.append(contentsOf: result.1) } self.showTags(notes: unlocked) DispatchQueue.main.async { if unlocked.count > 0 || (projects.count == 1 && isEmptyDir) { guard let vc = ViewController.shared() else { return } vc.notesTableView.disableLockedProject() vc.updateTable() { if action == "menu.newNote" { DispatchQueue.main.async { _ = vc.createNote() } } } self.reloadData(forRowIndexes: self.selectedRowIndexes, columnIndexes: [0]) } else { self.wrongPassAlert() } } } private func wrongPassAlert() { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("Wrong password", comment: "") alert.beginSheetModal(for: self.window!) { (returnCode: NSApplication.ModalResponse) -> Void in } } private func hideTags(notes: [Note]) { var notesTags = [String]() for note in notes { let tags = note.tags note.tags.removeAll() for tag in tags { if !notesTags.contains(tag) { notesTags.append(tag) } } } DispatchQueue.main.async { self.removeTags(notesTags) } } private func showTags(notes: [Note]) { var notesTags = [String]() for note in notes { if note.tags.count == 0 { _ = note.scanContentTags().0 } for insertTag in note.tags { if !notesTags.contains(insertTag) { notesTags.append(insertTag) } } } DispatchQueue.main.async { self.addTags(notesTags) } } // MARK: Functions private func isAllowedDropIndex(srcProject: Project, dstProject: Project?, dstIndex: Int) -> Bool { guard let sidebarItems = self.sidebarItems else { return false } var srcIndex: Int? if dstProject != nil, let srcParent = srcProject.parent, !srcParent.isDefault { srcIndex = srcParent.child.firstIndex(where: { $0 === srcProject }) } else { srcIndex = sidebarItems.firstIndex(where: { $0 as? Project === srcProject }) } guard let srcIndex = srcIndex else { return false } if srcIndex == dstIndex || srcIndex + 1 == dstIndex { return false } // Allow child reordering if parent equal to dst if let dstProject = dstProject, dstProject === srcProject.parent { return true } if sidebarItems.indices.contains(dstIndex - 1), let proposedProject = sidebarItems[dstIndex - 1] as? Project, srcProject.parent === proposedProject.parent || (srcProject.isBookmark && proposedProject.parent?.isDefault == true) || (srcProject.parent?.isDefault == true && proposedProject.isBookmark) { return true } if sidebarItems.indices.contains(dstIndex), sidebarItems[dstIndex] as? Project == nil { return false } if sidebarItems.indices.contains(dstIndex + 1), let proposedProject = sidebarItems[dstIndex + 1] as? Project, srcProject.parent === proposedProject.parent || (srcProject.isBookmark && proposedProject.parent?.isDefault == true) || (srcProject.parent?.isDefault == true && proposedProject.isBookmark) { return true } return false } private func saveOrderFor(projects: [Project]) { var i = 0 for project in projects { project.settings.priority = i i += 1 project.saveSettings() } } public func removeTags(notes: [Note]) { guard let vc = ViewController.shared() else { return } var allNoteTags: Set = [] for note in vc.notesTableView.getNoteList() { for tag in note.tags { if !allNoteTags.contains(tag) { allNoteTags.insert(tag) } } } var allRemoveTags = [String]() for note in notes { for tag in note.tags { allRemoveTags.append(tag) } } var remove = [String]() for tag in allRemoveTags { if !allNoteTags.contains(tag) { remove.append(tag) } } removeTags(remove) } public func insertTags(note: Note) { var tags = [String]() for tag in note.tags { if !tags.contains(tag) { tags.append(tag) } } var sTags: Set = [] if let allSidebarTags = sidebarItems?.filter({ ($0 as? FSTag) != nil }).map({ ($0 as? FSTag)!.getFullName() }) { sTags = Set(allSidebarTags) } var insert = [String]() for tag in tags { if !sTags.contains(tag) { insert.append(tag) } } addTags(insert) } private func isChangedSidebarItemsState() -> Bool { let sidebarItems = getSidebarItems() let selectedItems = selectedSidebarItems selectedSidebarItems = sidebarItems if let current = sidebarItems, let selected = selectedItems { for item in current { if !selected.contains(where: { $0 === item }) { return true } } return false } return sidebarItems?.count != selectedItems?.count } private func isChangedProjectsState() -> Bool { let sidebarProjects = getSelectedProjects() let selectedItems = selectedProjects selectedProjects = sidebarProjects if let current = sidebarProjects, let selected = selectedItems { for item in current { if !selected.contains(where: { $0 === item }) { return true } } return false } return sidebarProjects?.count != selectedItems?.count } private func isChangedTagsState() -> Bool { let sidebarTags = getSidebarTags() let selectedItems = selectedTags selectedTags = sidebarTags if let current = sidebarTags, let selected = selectedTags { for item in current { if !selected.contains(item) { return true } } return false } return sidebarTags?.count != selectedItems?.count } public func remove(project: Project) { selectedProjects?.removeAll(where: { $0 === project }) if UserDataService.instance.lastProject?.path == project.url.path { self.viewDelegate?.cleanSearchAndEditArea() selectRowIndexes(IndexSet(integer: 0), byExtendingSelection: false) } storage.cleanCachedTree(url: project.url) storage.removeBy(project: project) guard let vc = ViewController.shared(), vc.isVisibleSidebar() else { return } if let parent = project.parent, !parent.isDefault { if let index = parent.child.firstIndex(of: project) { parent.child.removeAll(where: { $0 == project }) removeItems(at: [index], inParent: parent, withAnimation: .effectFade) reloadItem(parent) } } else { if let index = sidebarItems?.firstIndex(where: { ($0 as? Project) == project }) { sidebarItems?.remove(at: index) removeItems(at: [index], inParent: nil, withAnimation: .effectFade) } } } public func insertRows(projects: [Project]) { for project in projects { insert(project: project) } storage.loadProjectRelations() } public func removeRows(projects: [Project]) { // Append and remove childs too if exist var projects = projects for item in projects { let child = item.getChildProjectsByURL() for childItem in child { // No project with url if projects.first(where: { $0.url.path == childItem.url.path }) == nil { projects.append(childItem) } } } for project in projects { // Remove notes from NoteTableView let notes = project.getNotes() viewDelegate?.notesTableView.removeRows(notes: notes) // Remove projects from SidebarOutlineView remove(project: project) } storage.loadProjectRelations() } public func insert(project: Project) { guard let vc = ViewController.shared(), vc.isVisibleSidebar(), let lastProjectIndex = vc.sidebarOutlineView.getProjectsSeparatorPosition() else { return } if let parent = storage.findParent(url: project.url) { if parent.isDefault { let offset = lastProjectIndex + countProjects() + 1 vc.sidebarOutlineView.sidebarItems?.insert(project, at: offset) vc.sidebarOutlineView.insertItems(at: [offset], inParent: nil, withAnimation: .effectFade) } else { if parent.child.filter({ $0.url == project.url }).count == 0 { parent.child.insert(project, at: 0) vc.sidebarOutlineView.insertItems(at: [0], inParent: parent, withAnimation: .effectFade) } vc.sidebarOutlineView.reloadItem(parent) } } else { let offset = lastProjectIndex + countProjects() + 1 vc.sidebarOutlineView.sidebarItems?.insert(project, at: offset) vc.sidebarOutlineView.insertItems(at: [offset], inParent: nil, withAnimation: .effectFade) } viewDelegate?.fsManager?.reloadObservedFolders() } public func addRoot() { let openPanel = NSOpenPanel() openPanel.allowsMultipleSelection = false openPanel.canChooseDirectories = true openPanel.canCreateDirectories = true openPanel.canChooseFiles = false openPanel.begin { (result) -> Void in if result == .OK { guard let url = openPanel.url else { return } let bookmarksManager = SandboxBookmark.sharedInstance() bookmarksManager.store(url: url) bookmarksManager.save() if let results = self.storage.insert(url: url, bookmark: true) { self.insertRows(projects: results) if let vc = self.viewDelegate { vc.fsManager?.restart() } } } } } public func getSidebarItems() -> [SidebarItem]? { var items = [SidebarItem]() for i in selectedRowIndexes { if let project = item(atRow: i) as? SidebarItem { items.append(project) } } return items } public func getSidebarProjects() -> [Project]? { guard let vc = ViewController.shared(), let v = vc.sidebarOutlineView else { return nil } var projects = [Project]() for i in v.selectedRowIndexes { if let si = item(atRow: i) as? SidebarItem, let project = si.project, !project.isVirtual, si.tag == nil { projects.append(project) } } for i in v.selectedRowIndexes { if let project = item(atRow: i) as? Project { projects.append(project) } } for project in projects { if project.settings.showNestedFoldersContent, !project.isEncrypted, let child = project.getAllChild() { for item in child { if !projects.contains(item) { projects.append(item) } } } } if projects.count > 0 { return projects } return nil } public func getSidebarTags() -> [String]? { guard let vc = ViewController.shared(), let v = vc.sidebarOutlineView else { return nil } var tags = [String]() for i in v.selectedRowIndexes { if let tag = (item(atRow: i) as? FSTag)?.getFullName() { tags.append(tag) } } if tags.count > 0 { return tags } return nil } public func getRawSidebarTags() -> [FSTag]? { guard let vc = ViewController.shared(), let v = vc.sidebarOutlineView else { return nil } var tags = [FSTag]() for i in v.selectedRowIndexes { if let tag = (item(atRow: i) as? FSTag) { tags.append(tag) } } if tags.count > 0 { return tags } return nil } public func getSelectedInlineTags() -> String { var inlineTags = String() if let tags = getSidebarTags() { for tag in tags { inlineTags += "#\(tag) " } } return inlineTags } public func selectNext() { let i = selectedRow + 1 guard let si = sidebarItems, si.indices.contains(i) else { return } if let next = si[i] as? SidebarItem { if next.type == .Separator && next.project == nil { let j = i + 1 guard let si = sidebarItems, si.indices.contains(j) else { return } if let next = si[j] as? SidebarItem, next.type != .Separator { selectRowIndexes([j], byExtendingSelection: false) return } return } } selectRowIndexes([i], byExtendingSelection: false) } public func selectPrev() { let i = selectedRow - 1 guard let si = sidebarItems, si.indices.contains(i) else { return } if let next = si[i] as? SidebarItem { if next.type == .Separator && next.project == nil { let j = i - 1 guard let si = sidebarItems, si.indices.contains(j) else { return } if let next = si[j] as? SidebarItem, next.type != .Separator { selectRowIndexes([j], byExtendingSelection: false) return } return } } selectRowIndexes([i], byExtendingSelection: false) } public func getSelectedProject() -> Project? { guard let vc = ViewController.shared(), let v = vc.sidebarOutlineView else { return nil } if let project = v.item(atRow: v.selectedRow) as? Project { return project } if let sidebarItem = v.item(atRow: v.selectedRow) as? SidebarItem { if sidebarItem.type == .Inbox { return vc.storage.getDefault() } if let project = sidebarItem.project { return project } } return nil } public func getSelectedProjects() -> [Project]? { var items = [Project]() for i in selectedRowIndexes { if let project = item(atRow: i) as? Project { items.append(project) } } return items } private func getSelectedProjectsIndexes() -> [Int]? { var items = [Int]() for i in selectedRowIndexes { if item(atRow: i) as? Project != nil { items.append(i) } } return items } @objc public func reloadSidebar() { guard let vc = ViewController.shared() else { return } vc.fsManager?.reloadObservedFolders() vc.loadMoveMenu() let selected = vc.sidebarOutlineView.selectedRow vc.sidebarOutlineView.sidebarItems = Sidebar().getList() vc.sidebarOutlineView.reloadData() vc.sidebarOutlineView.selectRowIndexes([selected], byExtendingSelection: false) if let project = getSelectedProject(), project.isLocked() { vc.notesTableView.enableLockedProject() } vc.sidebarOutlineView.loadAllTags() } public func deselectAllTags() { guard let items = self.sidebarItems?.filter({($0 as? FSTag) != nil}) else { return } for item in items { let i = self.row(forItem: item) guard i > -1 else { continue } if let row = self.rowView(atRow: i, makeIfNecessary: false), let cell = row.view(atColumn: 0) as? SidebarCellView { cell.icon.image = NSImage(named: "sidebar_tag") } } } public func selectSidebar(type: SidebarItemType) { if let i = sidebarItems?.firstIndex(where: {($0 as? SidebarItem)?.type == type }) { selectRowIndexes([i], byExtendingSelection: false) } } public func selectSidebarRoot() { if let i = sidebarItems?.firstIndex(where: { ($0 as? Project)?.isDefault == true }) { selectRowIndexes([i], byExtendingSelection: false) } } public func select(note: Note) { let sidebarItem = sidebarItems?.first(where: {($0 as? SidebarItem)?.project == note.project || $0 as? Project == note.project }) var index = row(forItem: sidebarItem) if (index == -1) { var expandQueue = [Project]() var project = note.project while let parent = project.parent, isExpandable(parent) { project = parent expandQueue.append(project) } for item in expandQueue.reversed() { expandItem(item) } index = row(forItem: note.project) } if index > -1 { selectNote = note scrollRowToVisible(index) selectRowIndexes([index], byExtendingSelection: false) return } } public func remove(tag: FSTag) { if let i = sidebarItems?.firstIndex(where: { ($0 as? FSTag) === tag }) { self.removeItems(at: [i], inParent: nil, withAnimation: []) sidebarItems?.remove(at: i) } } public func remove(tagName: String) { let tags = tagName.components(separatedBy: "/") guard let parent = tags.first else { return } if let vc = ViewController.shared(), !vc.isVisibleSidebar() { return } if let tag = sidebarItems?.first(where: {($0 as? FSTag)?.getName() == parent }) as? FSTag { if tags.count == 1 { let allTags = ViewController.shared()?.sidebarOutlineView.getAllTags() let count = allTags?.filter({ $0.starts(with: parent + "/") || $0 == parent }).count ?? 0 if count == 0 { if let index = sidebarItems?.firstIndex(where: { ($0 as? FSTag)?.getName() == parent }) { removeItems(at: [index], inParent: nil, withAnimation: []) sidebarItems?.remove(at: index) } } } else if var foundTag = tag.find(name: tagName) { while let parent = foundTag.getParent() { if let i = parent.indexOf(child: foundTag) { removeItems(at: [i], inParent: parent, withAnimation: []) parent.remove(by: i) } if parent.getParent() == nil && parent.child.count == 0, let i = sidebarItems?.firstIndex(where: { ($0 as? FSTag)?.getName() == parent.getName() }) { if isAllowTagRemoving(parent.getName()) { removeItems(at: [i], inParent: nil, withAnimation: []) sidebarItems?.remove(at: i) } break } foundTag = parent } } } } public func addTags(_ tags: [String], shouldUnloadOld: Bool = false) { guard tags.count > 0 else { unloadAllTags() return } beginUpdates() if shouldUnloadOld { unloadAllTags() } for tag in tags { addTag(tag: tag) } endUpdates() } public func removeTags(_ tags: [String]) { var removeTags = [String]() for tag in tags { if isAllowTagRemoving(tag) { removeTags.append(tag) } } beginUpdates() for tag in removeTags { remove(tagName: tag) } endUpdates() } public func isAllowTagRemoving(_ name: String) -> Bool { let tags = getAllTags() var allow = true for tag in tags { if tag.starts(with: name + "/") || tag == name { allow = false } } return allow } public func reloadTags() { if UserDefaultsManagement.inlineTags { loadAllTags() } } public func unloadAllTags() { if let tags = sidebarItems?.filter({ ($0 as? FSTag) != nil && ($0 as? FSTag)?.getParent() == nil }) as? [FSTag] { beginUpdates() for tag in tags { remove(tag: tag) } endUpdates() } } public func getAllTags() -> [String] { var tags: Set = [] var projects: [Project]? = getSidebarProjects() let selectedItem = item(atRow: selectedRow) as? SidebarItem if selectedItem?.type == .All || projects == nil { projects = storage.getProjects().filter({ !$0.isTrash && $0.settings.showInCommon }) } if let projects = projects { for project in projects { let projectTags = project.getAllTags() for tag in projectTags { if !tags.contains(tag) { tags.insert(tag) } } } } return Array(tags) } public func loadAllTags() { let tags = getAllTags() addTags(tags.sorted(), shouldUnloadOld: true) } public func select(tag: String) { let fullTags = tag.split(separator: "/").map(String.init); var items = sidebarItems; var tagDepth: Int = 0 var selectedIndexes = getSelectedProjectsIndexes() ?? [tagDepth] let currentNote = viewDelegate?.editor.note selectNote = currentNote for tagIndex in 0.. 1 else { return } while subtags.count > 0 { subtags = Array(subtags.dropFirst()) tag.addChild(name: subtags.joined(separator: "/"), completion: { (tagItem, isExist, position) in tag = tagItem if !isExist { insertItems(at: [position], inParent: tagItem.getParent(), withAnimation: []) } }) guard subtags.count > 1 else { break } } return } let rootTag = FSTag(name: tag) let position = getRootTagPosition(for: rootTag) sidebarItems?.insert(rootTag, at: position) self.insertItems(at: [position], inParent: nil, withAnimation: []) } public func getRootTagPosition(for tag: FSTag) -> Int { guard let offset = sidebarItems?.firstIndex(where: { ($0 as? FSTag) != nil }) else { return sidebarItems?.count ?? 0 } guard var tags = sidebarItems?.filter({ $0 as? FSTag != nil }) as? [FSTag] else { return sidebarItems?.count ?? 0 } tags.append(tag) let sorted = tags.sorted(by: { $0.name.lowercased() < $1.name.lowercased() }) if let index = sorted.firstIndex(where: { $0 === tag }) { return index + offset } return sidebarItems?.count ?? 0 } public func getTagsSeparatorPosition() -> Int? { return sidebarItems?.firstIndex(where: { ($0 as? SidebarItem)?.type == .Separator && ($0 as? SidebarItem)?.name == "tags" }) } public func getProjectsSeparatorPosition() -> Int? { return sidebarItems?.firstIndex(where: { ($0 as? SidebarItem)?.type == .Separator && ($0 as? SidebarItem)?.name == "projects" }) } public func countProjects() -> Int { return sidebarItems?.filter({ ($0 as? Project) != nil }).count ?? 0 } public func deleteRoot(tag: String) { guard let vc = ViewController.shared(), vc.isVisibleSidebar() else { return } let subtags = tag.components(separatedBy: "/") if let sidebarIndex = sidebarItems?.firstIndex(where: { ($0 as? FSTag)?.name == subtags.first }) { sidebarItems?.remove(at: sidebarIndex) removeItems(at: [sidebarIndex], inParent: nil, withAnimation: []) } } public func remove(tags: [String], from notes: [Note]) { guard let notesTableView = viewDelegate?.notesTableView else { return } for note in notes { for tagName in tags.reversed() { note.delete(tag: "#\(tagName)") note.tags.removeAll(where: { $0 == tagName }) _ = note.scanContentTags() } DispatchQueue.main.async { notesTableView.reloadRow(note: note) } } if let vc = ViewController.shared(), vc.isVisibleSidebar() { beginUpdates() for index in selectedRowIndexes.reversed() { if let tag = item(atRow: index) as? FSTag { if let parentTag = tag.getParent() { if let childIndex = tag.getParent()?.child.firstIndex(where: { $0 === tag }) { tag.parent?.removeChild(tag: tag) removeItems(at: [childIndex], inParent: parentTag, withAnimation: []) } } else if let sidebarIndex = sidebarItems?.firstIndex(where: { ($0 as? FSTag) === tag }) { sidebarItems?.remove(at: sidebarIndex) removeItems(at: [sidebarIndex], inParent: nil, withAnimation: []) } } } endUpdates() } viewDelegate?.editor.clear() } public func rename(tags: [FSTag], name: String) { guard let notesTableView = viewDelegate?.notesTableView else { return } let notes = notesTableView.getNoteList() let originalName = name.starts(with: "#") ? String(name.dropFirst()) : name let name = name.starts(with: "#") ? name : "#\(name)" var insertTags = [String]() var deleteTags = [String]() // get all root deleted tags and all inserted from roots combined with renamed for tag in tags { let tagNameOriginal = tag.getFullName() var fullName = tagNameOriginal let firstLevel = fullName.components(separatedBy: "/").first ?? fullName deleteTags.append(fullName) let allTags = getAllTags() // select all started from "#search/level/" or equal "#search/level" let related = allTags.filter({ $0.starts(with: fullName + "/") || $0 == fullName }) // select all started i.e. "#search/yyy" but NOT "#search/level/" and "#search/level" let relatedAdditional = allTags.filter({ $0.starts(with: firstLevel + "/") && !$0.starts(with: fullName + "/") && $0 != fullName }) // rename related for item in related { fullName = item guard let range = fullName.range(of: tagNameOriginal) else { continue } if range.lowerBound.utf16Offset(in: tagNameOriginal) == 0 { fullName.replaceSubrange(range, with: originalName) } insertTags.append(fullName) } // and add additional for item in relatedAdditional { insertTags.append(item) } } // rename tags in notes for note in notes { for tag in tags { // rename and rescan tags ended with empty space separators or slash and skip with chars let tagName = tag.getFullName() note.replace(tag: "#\(tagName)", with: name) note.tags.removeAll(where: { $0 == tagName }) _ = note.scanContentTags() // reload view in notes list DispatchQueue.main.async { notesTableView.reloadRow(note: note) } } } // update view beginUpdates() for tag in deleteTags { deleteRoot(tag: tag) } for tag in insertTags { addTag(tag: tag) } endUpdates() // select inserted if let tag = insertTags.first?.components(separatedBy: "/").first { if let tag = sidebarItems?.first(where: { ($0 as? FSTag)?.name == tag }) { let index = row(forItem: tag) scrollRowToVisible(index) selectRowIndexes([index], byExtendingSelection: true) } } } public func deselectAllRows() { UserDefaultsManagement.lastSidebarItem = nil UserDefaultsManagement.lastProjectURL = nil deselectAll(nil) } public func getNotesProject() -> Project? { let item = sidebarItems?.first(where: { ($0 as? SidebarItem)?.type == .All }) as? SidebarItem return item?.project } public func getOrCreateProject(name: String) -> Project? { guard let project = Storage.shared().getDefault() else { return nil } let url = project.url.appendingPathComponent(name, isDirectory: true) if let exist = Storage.shared().getProjectBy(url: url) { DispatchQueue.main.async { self.focus(on: exist) } return exist } return createProject(with: name) } public func createProject(in project: Project? = nil, with name: String) -> Project? { guard let vc = ViewController.shared(), let project = project ?? Storage.shared().getDefault() else { return nil } var insertedProject: Project? do { let projectURL = project.url.appendingPathComponent(name, isDirectory: true) try FileManager.default.createDirectory(at: projectURL, withIntermediateDirectories: false, attributes: nil) guard let inserted = project.storage.insert(url: projectURL) else { return nil } insertedProject = inserted.first // Important before main queue (Disables the fake move event handler for notes) vc.fsManager?.reloadObservedFolders() DispatchQueue.main.async { vc.sidebarOutlineView.insertRows(projects: inserted) guard let newProject = inserted.first else { return } self.focus(on: newProject) print("sidebar table") } } catch { DispatchQueue.main.async { let alert = NSAlert() alert.messageText = error.localizedDescription alert.runModal() } } return insertedProject } public func focus(on project: Project) { guard let vc = ViewController.shared() else { return } let expand = project.parent vc.sidebarOutlineView.expandItem(expand) let row = vc.sidebarOutlineView.row(forItem: project) guard row != -1 else { return } vc.sidebarOutlineView.selectRowIndexes( IndexSet(integer: row), byExtendingSelection: false ) vc.sidebarOutlineView.scrollRowToVisible(row) } } ================================================ FILE: FSNotes/View/SidebarSplitView.swift ================================================ // // SidebarSplitView.swift // FSNotes // // Created by Oleksandr Glushchenko on 9/29/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa class SidebarSplitView: NSSplitView { override var dividerColor: NSColor { return NSColor.init(named: "divider")! } } ================================================ FILE: FSNotes/View/SidebarTableRowView.swift ================================================ // // SidebarTableRowView.swift // FSNotes // // Created by Oleksandr Glushchenko on 4/11/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa class SidebarTableRowView: NSTableRowView { } ================================================ FILE: FSNotes/View/TitleBarView.swift ================================================ // // TitleBarView.swift // FSNotes // // Created by BUDDAx2 on 10/27/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Cocoa class TitleBarView: NSView { var onMouseEnteredClosure: (()->())? var onMouseExitedClosure: (()->())? override func awakeFromNib() { addTrackingArea(NSTrackingArea(rect: bounds, options: [.activeAlways, .mouseEnteredAndExited], owner: self, userInfo: nil)) } override func layout() { super.layout() self.trackingAreas.forEach { [weak self] area in self?.removeTrackingArea(area) } addTrackingArea(NSTrackingArea(rect: bounds, options: [.activeAlways, .mouseEnteredAndExited], owner: self, userInfo: nil)) } override func mouseEntered(with event: NSEvent) { onMouseEnteredClosure?() } override func mouseExited(with event: NSEvent) { onMouseExitedClosure?() } override func mouseDown(with event: NSEvent) { if event.clickCount == 2, let actionOnDoubleClick = UserDefaults.standard.object(forKey: "AppleActionOnDoubleClick") as? String { switch actionOnDoubleClick { case "Maximize": self.window?.windowController?.maximizeWindow() case "Minimize": self.window?.performMiniaturize(nil) default: break } } } } ================================================ FILE: FSNotes/View/TitleTextField.swift ================================================ // // TitleTextField.swift // FSNotes // // Created by Олександр Глущенко on 5/10/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Cocoa import Carbon.HIToolbox class TitleTextField: NSTextField { public var restoreResponder: NSResponder? override func performKeyEquivalent(with event: NSEvent) -> Bool { if event.modifierFlags.contains(.command) && event.characters?.unicodeScalars.first == "c" && !event.modifierFlags.contains(.shift) && !event.modifierFlags.contains(.control) && !event.modifierFlags.contains(.option) { let pasteboard = NSPasteboard.general pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) pasteboard.setString(self.stringValue, forType: NSPasteboard.PasteboardType.string) } return super.performKeyEquivalent(with: event) } override func becomeFirstResponder() -> Bool { if let vc = ViewController.shared(), let note = vc.editor.note { stringValue = note.getFileName() } return super.becomeFirstResponder() } override func textDidEndEditing(_ notification: Notification) { guard stringValue.count > 0, let vc = ViewController.shared(), let note = vc.editor.note else { return } let currentTitle = stringValue let currentName = note.getFileName() defer { updateNotesTableView() editModeOff() } if currentName != currentTitle { rename(currentTitle: currentTitle, note: note) return } vc.updateTitle(note: note) self.resignFirstResponder() updateNotesTableView() vc.titleLabel.isEditable = false vc.titleLabel.isEnabled = false } public func rename(currentTitle: String, note: Note) { guard let vc = ViewController.shared() else { return } _ = vc.lockUnlocked(notes: [note]) let currentName = note.getFileName() let ext = note.url.pathExtension let fileName = currentTitle .trimmingCharacters(in: CharacterSet.whitespaces) .replacingOccurrences(of: ":", with: "") .replacingOccurrences(of: "/", with: "") let dst = note.project.url .appendingPathComponent(fileName) .appendingPathExtension(ext) let hasCaseSensitiveDiffOnly = currentName.lowercased() == fileName.lowercased() if !FileManager.default.fileExists(atPath: dst.path) || hasCaseSensitiveDiffOnly { _ = note.move(to: dst, forceRewrite: hasCaseSensitiveDiffOnly) vc.updateTitle(note: note) updateNotesTableView() vc.reSort(note: note) } else { vc.updateTitle(note: note) self.resignFirstResponder() updateNotesTableView() vc.titleLabel.isEditable = false vc.titleLabel.isEnabled = false let alert = NSAlert() let informativeText = NSLocalizedString("Note with name \"%@\" already exists in selected directory.", comment: "") alert.alertStyle = .critical alert.informativeText = String(format: informativeText, currentTitle) alert.runModal() } } public func editModeOn() { self.isEnabled = true self.isEditable = true MainWindowController.shared()?.makeFirstResponder(self) } public func editModeOff() { self.isEnabled = false self.isEditable = false guard let vc = ViewController.shared(), let note = vc.editor.note else { return } vc.updateTitle(note: note) } public func updateNotesTableView() { guard let vc = ViewController.shared(), let note = vc.editor.note else { return } if (note.container == .encryptedTextPack && !note.isUnlocked()) || !note.project.settings.isFirstLineAsTitle() { vc.notesTableView.reloadRow(note: note) } if let responder = restoreResponder { window?.makeFirstResponder(responder) } } } ================================================ FILE: FSNotes/View/VerticallyAlignedTextFieldCell.swift ================================================ // // VerticallyAlignedTextFieldCell.swift // FSNotes // // Created by Олександр Глущенко on 03.05.2020. // Copyright © 2020 Oleksandr Glushchenko. All rights reserved. // import Cocoa class VerticallyAlignedTextFieldCell: NSTextFieldCell { override func drawingRect(forBounds rect: NSRect) -> NSRect { let newRect = NSRect(x: 0, y: (rect.size.height - 22) / 2, width: rect.size.width, height: 22) return super.drawingRect(forBounds: newRect) } } ================================================ FILE: FSNotes/ViewController+Git.swift ================================================ // // ViewController+Git.swift // FSNotes // // Created by Олександр Глущенко on 9/10/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Cocoa import Git import Cgit2 extension EditorViewController { @IBAction func saveRevision(_ sender: NSMenuItem) { guard let gitProject = getGitProject() else { let alert = NSAlert() alert.alertStyle = .critical alert.informativeText = NSLocalizedString("Please init git repository before (Preferences -> Git -> Init/commit)", comment: "") alert.messageText = NSLocalizedString("Repository not found", comment: "") alert.runModal() return } guard let window = self.view.window else { return } if UserDefaultsManagement.askCommitMessage { let field = NSTextField(frame: NSRect(x: 0, y: 0, width: 290, height: 60)) if let lastMessage = UserDefaultsManagement.lastCommitMessage { field.stringValue = lastMessage } let alert = NSAlert() alert.messageText = NSLocalizedString("Commit message:", comment: "") alert.accessoryView = field alert.alertStyle = .informational alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) alert.beginSheetModal(for: window) { (returnCode: NSApplication.ModalResponse) -> Void in if returnCode == NSApplication.ModalResponse.alertFirstButtonReturn { let commitMessage: String? = field.stringValue.count > 0 ? field.stringValue : nil if field.stringValue.count > 0 { UserDefaultsManagement.lastCommitMessage = commitMessage } self.saveRevision(project: gitProject, commitMessage: commitMessage) } } field.becomeFirstResponder() return } saveRevision(project: gitProject, commitMessage: nil) } private func saveRevision(project: Project, commitMessage: String? = nil) { guard let window = self.view.window else { return } ViewController.gitQueue.addOperation({ ViewController.gitQueueOperationDate = Date() defer { ViewController.gitQueueOperationDate = nil } do { try project.saveRevision(commitMessage: commitMessage) } catch GitError.noAddedFiles { // pass } catch { var message = String() if let error = error as? GitError { message = error.associatedValue() } else { message = error.localizedDescription } DispatchQueue.main.async { let alert = NSAlert() alert.alertStyle = .critical alert.informativeText = message alert.messageText = NSLocalizedString("Git error", comment: "") alert.beginSheetModal(for: window) { (returnCode: NSApplication.ModalResponse) -> Void in } } } }) } @IBAction func checkoutRevision(_ sender: NSMenuItem) { guard let vc = ViewController.shared() else { return } guard let commit = sender.representedObject as? Commit else { return } guard let note = vcEditor?.note else { return } if vc.prevCommit == nil { saveRevision(project: note.project, commitMessage: "Auto save on history checkout") } vc.prevCommit = commit note.checkout(commit: commit) _ = note.reload() NotesTextProcessor.highlight(attributedString: note.content) reloadAllOpenedWindows(note: note) ViewController.shared()?.notesTableView.reloadRow(note: note) vcEditor?.scanTagsAndAutoRename() } @IBAction private func makeFullSnapshot(_ sender: Any) { let cal = Calendar.current let hour = cal.component(.hour, from: Date()) let minute = cal.component(.minute, from: Date()) if let lastSnapshot = self.lastSnapshot { if minute == lastSnapshot { return } else { self.lastSnapshot = nil } } guard UserDefaultsManagement.snapshotsInterval != 0 && ( hour == UserDefaultsManagement.snapshotsInterval || ( hour != 0 && hour % UserDefaultsManagement.snapshotsInterval == 0 ) ) else { return } guard UserDefaultsManagement.snapshotsIntervalMinutes == minute else { return } lastSnapshot = minute ViewController.gitQueue.addOperation({ ViewController.gitQueueOperationDate = Date() defer { ViewController.gitQueueOperationDate = nil } let storage = Storage.shared() guard let projects = storage.getGitProjects() else { return } for project in projects { do { if project.hasRepository() { try project.commit() try project.pull() try project.push() } } catch { print(error) } } }) } @IBAction private func pull(_ sender: Any) { // Restart queue if operation stucked more then 2 minutes if let date = ViewController.gitQueueOperationDate { let diff = Int(Date().timeIntervalSince1970) - Int(date.timeIntervalSince1970) let isBusy = ViewController.gitQueueBusy if diff > 120 && !isBusy { ViewController.gitQueue = OperationQueue() ViewController.gitQueue.maxConcurrentOperationCount = 1 print("Git queue restart") } else { print("Git pull skipped") return } } ViewController.gitQueue.addOperation({ ViewController.gitQueueOperationDate = Date() defer { ViewController.gitQueueOperationDate = nil } Storage.shared().pullAll() }) } public func scheduleSnapshots() { guard !UserDefaultsManagement.backupManually else { return } DispatchQueue.main.async { self.snapshotsTimer.invalidate() self.snapshotsTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.makeFullSnapshot), userInfo: nil, repeats: true) } } public func schedulePull() { guard !UserDefaultsManagement.backupManually else { return } let interval = UserDefaultsManagement.pullInterval pullTimer.invalidate() pullTimer = Timer.scheduledTimer(timeInterval: TimeInterval(interval), target: self, selector: #selector(pull), userInfo: nil, repeats: true) } public func stopPull() { pullTimer.invalidate() } public func getGitProject() -> Project? { guard let vc = ViewController.shared() else { return nil } if let project = vc.getSelectedNote()?.project.getGitProject() { return project } if let project = vc.sidebarOutlineView.getSelectedProject()?.getGitProject() { return project } return Storage.shared().getDefault()?.getGitProject() } } ================================================ FILE: FSNotes/ViewController+Menu.swift ================================================ // // ViewController+Menu.swift // FSNotes // // Created by Oleksandr Hlushchenko on 16.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import AppKit extension ViewController { func processFileMenuItems(_ menuItem: NSMenuItem, menuId: String) -> Bool { // Submenu if menuItem.menu?.identifier?.rawValue == "fileMenu.move" || menuItem.menu?.identifier?.rawValue == "fileMenu.history" { return true } guard let vc = ViewController.shared(), let evc = NSApplication.shared.keyWindow?.contentViewController as? EditorViewController, let id = menuItem.identifier?.rawValue else { return false } // Sidebar let tags = vc.sidebarOutlineView.getSidebarTags() let projects = vc.sidebarOutlineView.getSelectedProjects() let projectSelected = projects?.isEmpty == false let tagSelected = tags?.isEmpty == false let isFirstSidebar = evc.view.window?.firstResponder?.isKind(of: SidebarOutlineView.self) == true let isInbox = vc.sidebarOutlineView.getSidebarItems()?.first?.type == .Inbox let isTrash = vc.sidebarOutlineView.getSidebarItems()?.first?.type == .Trash // Notes let isFirstResponder = evc.view.window?.firstResponder?.isKind(of: NotesTableView.self) == true let isFirstEditor = evc.view.window?.firstResponder?.isKind(of: EditTextView.self) == true let isOpenedWindow = NSApplication.shared.keyWindow?.contentViewController?.isKind(of: NoteViewController.self) == true let notes = vc.getSelectedNotes() let greaterThanZero = notes?.isEmpty == false let isOne = notes?.count == 1 func hasEncrypted(notes: [Note]? = nil) -> Bool { guard let notes = notes else { return false } return notes.contains { $0.isEncrypted() && !$0.project.isEncrypted } } switch id { case "\(menuId).close": menuItem.title = NSLocalizedString("Close", comment: "File Menu") return true case "\(menuId).import": menuItem.title = NSLocalizedString("Import", comment: "File Menu") return true case "\(menuId).attach": menuItem.title = NSLocalizedString("Add External Folder...", comment: "Menu Library") return true case "\(menuId).backup": var title = NSLocalizedString("Inbox", comment: "") if let gitProject = vc.getGitProject() { title = gitProject.label if gitProject.isDefault { title = NSLocalizedString("Inbox", comment: "") } menuItem.title = String(format: NSLocalizedString("Commit & Push “%@”", comment: "Menu Library"), title) return true } return false case "\(menuId).new": menuItem.title = NSLocalizedString("New Note", comment: "File Menu") return true case "\(menuId).newInNewWindow": menuItem.title = NSLocalizedString("New Note in New Window", comment: "File Menu") return true case "\(menuId).createFolder": menuItem.title = NSLocalizedString("New Folder", comment: "Menu Library") return !isTrash case "\(menuId).searchAndCreate": menuItem.title = NSLocalizedString("Search and Create", comment: "File Menu") return true case "\(menuId).open": menuItem.title = NSLocalizedString("Open Note in New Window", comment: "File Menu") return greaterThanZero case "\(menuId).duplicate": menuItem.title = NSLocalizedString("Duplicate", comment: "File Menu") return greaterThanZero && (isFirstResponder || isFirstEditor) case "\(menuId).rename": // sidebar if isFirstSidebar { if tagSelected { menuItem.title = NSLocalizedString("Rename Tag", comment: "Menu Library") } else { menuItem.title = NSLocalizedString("Rename Folder", comment: "Menu Library") } return projectSelected || tagSelected } menuItem.title = NSLocalizedString("Rename", comment: "File Menu") return isOne && isFirstResponder || (isFirstEditor && !isOpenedWindow) case "\(menuId).delete": menuItem.title = NSLocalizedString("Delete", comment: "File Menu") return greaterThanZero && isFirstResponder case "\(menuId).forceDelete": menuItem.title = NSLocalizedString("Force Delete", comment: "File Menu") return greaterThanZero && isFirstResponder case "\(menuId).togglePin": if let note = notes?.first, note.isPinned { menuItem.title = NSLocalizedString("Unpin", comment: "File Menu") } else { menuItem.title = NSLocalizedString("Pin", comment: "File Menu") } return greaterThanZero case "\(menuId).decrypt": // sidebar if isFirstSidebar { menuItem.title = NSLocalizedString("Decrypt Folder", comment: "Menu Library") if let project = projects?.first, !project.isTrash, !project.isDefault, !project.isVirtual, project.isEncrypted { return true } return false } menuItem.title = NSLocalizedString("Decrypt", comment: "File Menu") return greaterThanZero && hasEncrypted(notes: notes) case "\(menuId).toggleLock": // sidebar if isFirstSidebar { if let project = projects?.first, !project.isTrash, project.isLocked() { menuItem.title = NSLocalizedString("Unlock Folder", comment: "") } else { menuItem.title = NSLocalizedString("Lock Folder", comment: "Menu Library") } return projectSelected } if let note = notes?.first, note.isEncryptedAndLocked() { menuItem.title = NSLocalizedString("Unlock", comment: "File Menu") } else { menuItem.title = NSLocalizedString("Lock", comment: "File Menu") } return greaterThanZero && (isFirstResponder || isOpenedWindow || isFirstEditor) case "\(menuId).external": menuItem.title = NSLocalizedString("Open External", comment: "File Menu") return greaterThanZero case "\(menuId).reveal": if isFirstSidebar { menuItem.title = NSLocalizedString("Reveal in Finder", comment: "Menu Library") return projectSelected || isInbox } menuItem.title = NSLocalizedString("Reveal in Finder", comment: "File Menu") return greaterThanZero && (isFirstResponder || isOpenedWindow || isFirstEditor) case "\(menuId).date": menuItem.title = NSLocalizedString("Change Creation Date", comment: "File Menu") return greaterThanZero && (isFirstResponder || isOpenedWindow || isFirstEditor) case "\(menuId).toggleContainer": if let note = notes?.first, note.container == .none { menuItem.title = NSLocalizedString("Convert to TextBundle", comment: "") } else { menuItem.title = NSLocalizedString("Convert to Plain", comment: "") } return greaterThanZero && !hasEncrypted(notes: notes) && (isFirstResponder || isOpenedWindow) case "\(menuId).move": menuItem.title = NSLocalizedString("Move", comment: "File Menu") return greaterThanZero && (isFirstResponder || isOpenedWindow || isFirstEditor) case "\(menuId).history": menuItem.title = NSLocalizedString("History", comment: "File Menu") if let note = notes?.first { return isOne && (isFirstResponder || isOpenedWindow || isFirstEditor) && note.project.hasCommitsDiffsCache() } case "\(menuId).print": menuItem.title = NSLocalizedString("Print", comment: "File Menu") return isOne && (isFirstResponder || isOpenedWindow || isFirstEditor) default: break } return false } func processShareMenuItems(_ menuItem: NSMenuItem, menuId: String) -> Bool { guard let vc = ViewController.shared(), let evc = NSApplication.shared.keyWindow?.contentViewController as? EditorViewController, let id = menuItem.identifier?.rawValue else { return false } let isFirstResponder = evc.view.window?.firstResponder?.isKind(of: NotesTableView.self) == true let isFirstEditor = evc.view.window?.firstResponder?.isKind(of: EditTextView.self) == true let isOpenedWindow = NSApplication.shared.keyWindow?.contentViewController?.isKind(of: NoteViewController.self) == true let notes = vc.getSelectedNotes() let isOne = notes?.count == 1 switch id { case "\(menuId).copyURL": menuItem.title = NSLocalizedString("Copy URL", comment: "File Menu") return isOne && (isFirstResponder || isOpenedWindow || isFirstEditor) case "\(menuId).copyTitle": menuItem.title = NSLocalizedString("Copy Title", comment: "File Menu") return isOne && (isFirstResponder || isOpenedWindow || isFirstEditor) case "\(menuId).uploadOverSSH": if let note = notes?.first, note.uploadPath != nil || note.apiId != nil { menuItem.title = NSLocalizedString("Update Web Page", comment: "File Menu") } else { menuItem.title = NSLocalizedString("Create Web Page", comment: "File Menu") } return isOne && (isFirstResponder || isOpenedWindow || isFirstEditor) case "\(menuId).removeOverSSH": menuItem.title = NSLocalizedString("Delete Web Page", comment: "File Menu") if let note = notes?.first { return (isFirstResponder || isOpenedWindow || isFirstEditor) && isOne && !note.isEncrypted() && (note.uploadPath != nil || note.apiId != nil) } default: return false } return false } func processLibraryMenuItems(_ menuItem: NSMenuItem, menuId: String) -> Bool { guard let vc = ViewController.shared(), let id = menuItem.identifier?.rawValue else { return false } let tags = vc.sidebarOutlineView.getSidebarTags() let projects = vc.sidebarOutlineView.getSelectedProjects() let projectSelected = projects?.isEmpty == false let tagSelected = tags?.isEmpty == false let isFirstResponder = view.window?.firstResponder?.isKind(of: SidebarOutlineView.self) == true let isTrash = vc.sidebarOutlineView.getSidebarItems()?.first?.type == .Trash let isInbox = vc.sidebarOutlineView.getSidebarItems()?.first?.type == .Inbox let isSystem = vc.sidebarOutlineView.getSidebarItems()?.first?.isSystem() == true switch id { case "\(menuId).create": menuItem.title = NSLocalizedString("Create Folder", comment: "Menu Library") return !isTrash case "\(menuId).rename": if tagSelected { menuItem.title = NSLocalizedString("Rename Tag", comment: "Menu Library") } else { menuItem.title = NSLocalizedString("Rename Folder", comment: "Menu Library") } return isFirstResponder && (projectSelected || tagSelected) case "\(menuId).delete": if let project = projects?.first, project.isBookmark { menuItem.title = NSLocalizedString("Unlink External Folder", comment: "Menu Library") } else if tagSelected { menuItem.title = NSLocalizedString("Delete Tag", comment: "Menu Library") } else { menuItem.title = NSLocalizedString("Delete Folder", comment: "Menu Library") } return isFirstResponder && (projectSelected || tagSelected) case "\(menuId).decrypt": menuItem.title = NSLocalizedString("Decrypt Folder", comment: "Menu Library") if let project = projects?.first, !project.isTrash, !project.isDefault, !project.isVirtual, project.isEncrypted { return isFirstResponder } case "\(menuId).toggleLock": if let project = projects?.first, !project.isTrash, project.isLocked() { menuItem.title = NSLocalizedString("Unlock Folder", comment: "") } else { menuItem.title = NSLocalizedString("Lock Folder", comment: "Menu Library") } return isFirstResponder && projectSelected case "\(menuId).reveal": menuItem.title = NSLocalizedString("Reveal in Finder", comment: "Menu Library") return isFirstResponder && (projectSelected || isInbox) case "\(menuId).options": menuItem.title = NSLocalizedString("Show Options", comment: "Menu Library") return isFirstResponder && (projectSelected || isSystem) default: break } return false } func loadMoveMenu() { guard let vc = ViewController.shared(), let note = vc.notesTableView.getSelectedNote() else { return } let moveTitle = NSLocalizedString("Move", comment: "Menu") if let prevMenu = noteMenu.item(withTitle: moveTitle) { noteMenu.removeItem(prevMenu) } let moveMenuItem = NSMenuItem() moveMenuItem.title = NSLocalizedString("Move", comment: "Menu") moveMenuItem.image = NSImage(systemSymbolName: "move.3d", accessibilityDescription: nil) noteMenu.addItem(moveMenuItem) let moveMenu = NSMenu() moveMenu.identifier = NSUserInterfaceItemIdentifier("fileMenu.move") if UserDefaultsManagement.inlineTags, let tagsMenu = noteMenu.item(withTitle: NSLocalizedString("Tags", comment: "")) { noteMenu.removeItem(tagsMenu) } if !note.isTrash() { let trashMenu = NSMenuItem() trashMenu.title = NSLocalizedString("Trash", comment: "Sidebar label") trashMenu.action = #selector(vc.notesTableView.delete(_:)) trashMenu.tag = 555 moveMenu.addItem(trashMenu) moveMenu.addItem(NSMenuItem.separator()) } let projects = storage.getSortedProjects() for item in projects { if note.project == item || item.isTrash { continue } let menuItem = NSMenuItem() menuItem.title = item.getNestedLabel() menuItem.representedObject = item menuItem.action = #selector(vc.moveNote(_:)) moveMenu.addItem(menuItem) } noteMenu.setSubmenu(moveMenu, for: moveMenuItem) loadHistory() } public func loadHistory() { guard let vc = ViewController.shared(), let notes = vc.notesTableView.getSelectedNotes(), let note = notes.first else { return } let title = NSLocalizedString("History", comment: "") let historyMenu = noteMenu.item(withTitle: title) historyMenu?.submenu?.removeAllItems() historyMenu?.isEnabled = false historyMenu?.isHidden = !note.project.hasCommitsDiffsCache() guard notes.count == 0x01 else { return } DispatchQueue.global().async { let commits = note.getCommits() DispatchQueue.main.async { guard commits.count > 0 else { historyMenu?.isEnabled = false return } for commit in commits { let menuItem = NSMenuItem() menuItem.title = commit.getDate() menuItem.representedObject = commit menuItem.action = #selector(vc.checkoutRevision(_:)) historyMenu?.submenu?.addItem(menuItem) } historyMenu?.isEnabled = true } } } } ================================================ FILE: FSNotes/ViewController+Print.swift ================================================ // // ViewController+Print.swift // FSNotes // // Created by Oleksandr Glushchenko on 2/15/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import WebKit extension EditorViewController { public func printMarkdownPreview() { guard let note = vcEditor?.note else { return } let printDir = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("Print") try? FileManager.default.removeItem(at: printDir) guard let indexURL = MPreviewView.buildPage(for: note, at: printDir, print: true) else { return } if #available(macOS 11.0, *) { let pdfCreator = Printer(indexURL: indexURL) pdfCreator.printWeb() } else { legacyPrint(indexURL: indexURL) } } @available(*, deprecated, message: "Remove after macOS 10.15 is no longer supported") public func legacyPrint(indexURL: URL) { guard let vc = ViewController.shared() else { return } vc.printerLegacy = PrinterLegacy(indexURL: indexURL) vc.printerLegacy?.printWeb() } } ================================================ FILE: FSNotes/ViewController+Web.swift ================================================ // // ViewController+Web.swift // FSNotes // // Created by Oleksandr Hlushchenko on 15.09.2022. // Copyright © 2022 Oleksandr Hlushchenko. All rights reserved. // import Cocoa import Shout extension EditorViewController { public func getCurrentNote() -> Note? { return vcEditor?.note } @IBAction func removeWebNote(_ sender: NSMenuItem) { if !UserDefaultsManagement.customWebServer, let note = getCurrentNote() { ViewController.shared()?.deleteAPI(note: note, completion: { DispatchQueue.main.async { ViewController.shared()?.notesTableView.reloadRow(note: note) } }) return } guard let note = getCurrentNote(), let remotePath = note.uploadPath else { return } DispatchQueue.global().async { do { guard let ssh = self.getSSHResource() else { return } try ssh.execute("rm -r \(remotePath)") note.uploadPath = nil Storage.shared().saveUploadPaths() DispatchQueue.main.async { ViewController.shared()?.notesTableView.reloadRow(note: note) } } catch { print(error, error.localizedDescription) } } } @IBAction func uploadWebNote(_ sender: NSMenuItem) { if !UserDefaultsManagement.customWebServer, let note = getCurrentNote() { ViewController.shared()?.createAPI(note: note, completion: { url in DispatchQueue.main.async { ViewController.shared()?.notesTableView.reloadRow(note: note) guard let url = url else { return } let pasteboard = NSPasteboard.general pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) pasteboard.setString(url.absoluteString, forType: NSPasteboard.PasteboardType.string) NSWorkspace.shared.open(url) } }) return } guard let note = getCurrentNote() else { return } let dst = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("Upload") try? FileManager.default.removeItem(at: dst) guard let localURL = MPreviewView.buildPage(for: note, at: dst, web: true), let sftpPath = UserDefaultsManagement.sftpPath, let web = UserDefaultsManagement.sftpWeb else { return } let latinName = note.getLatinName() let remoteDir = "\(sftpPath)\(latinName)/" let resultUrl = web + latinName + "/" NSPasteboard.general.clearContents() NSPasteboard.general.setString(web + latinName + "/", forType: .string) let images = note.content.getImagesAndFiles() DispatchQueue.global().async { do { guard let ssh = self.getSSHResource() else { return } try ssh.execute("mkdir -p \(remoteDir)") let zipURL = localURL .deletingLastPathComponent() .appendingPathComponent(note.getLatinName()) .appendingPathExtension("zip") let sftp = try ssh.openSftp() // Upload index.html let remoteIndex = remoteDir + "index.html" _ = try ssh.execute("rm -r \(remoteIndex)") try sftp.upload(localURL: localURL, remotePath: remoteIndex) // Upload archive try? sftp.upload(localURL: zipURL, remotePath: remoteDir + note.getLatinName() + ".zip") // Upload images var imageDirCreationDone = false for image in images { if image.path.startsWith(string: "http://") || image.path.startsWith(string: "https://") { continue } if !imageDirCreationDone { try ssh.execute("mkdir -p \(remoteDir)/i") imageDirCreationDone = true } try? sftp.upload(localURL: image.url, remotePath: remoteDir + "i/" + image.url.lastPathComponent) } if #available(macOS 10.14, *) { DispatchQueue.main.async { ViewController.shared()?.sendNotification() ViewController.shared()?.notesTableView.reloadRow(note: note) NSWorkspace.shared.open(URL(string: resultUrl)!) } } print("Upload was successfull for note: \(note.title)") note.uploadPath = remoteDir Storage.shared().saveUploadPaths() } catch { print(error, error.localizedDescription) } } } private func getSSHResource() -> SSH? { let host = UserDefaultsManagement.sftpHost let port = UserDefaultsManagement.sftpPort let username = UserDefaultsManagement.sftpUsername let password = UserDefaultsManagement.sftpPassword let passphrase = UserDefaultsManagement.sftpPassphrase var publicKeyURL: URL? var privateKeyURL: URL? if let accessData = UserDefaultsManagement.sftpAccessData, let bookmarks = NSKeyedUnarchiver.unarchiveObject(with: accessData) as? [URL: Data] { for bookmark in bookmarks { if bookmark.key.path.hasSuffix(".pub") { publicKeyURL = bookmark.key } else { privateKeyURL = bookmark.key } } } if password.count == 0, publicKeyURL == nil || publicKeyURL == nil { uploadError(text: "Please set private and public keys") return nil } do { let ssh = try SSH(host: host, port: port) if password.count > 0 { try ssh.authenticate(username: username, password: password) } else if let publicKeyURL = publicKeyURL, let privateKeyURL = privateKeyURL { try ssh.authenticate(username: username, privateKey: privateKeyURL.path, publicKey: publicKeyURL.path, passphrase: passphrase) } return ssh } catch { print(error, error.localizedDescription) return nil } } public func uploadError(text: String) { let alert = NSAlert() alert.alertStyle = .critical alert.informativeText = NSLocalizedString("Upload error", comment: "") alert.messageText = text alert.beginSheetModal(for: self.view.window!) } public func showAlert(message: String) { DispatchQueue.main.async { let alert = NSAlert() alert.alertStyle = .critical alert.informativeText = NSLocalizedString(message, comment: "") alert.messageText = NSLocalizedString("Web publishing error", comment: "") alert.beginSheetModal(for: self.view.window!) { (returnCode: NSApplication.ModalResponse) -> Void in } } } } ================================================ FILE: FSNotes/ViewController.swift ================================================ // // ViewController.swift // FSNotes // // Created by Oleksandr Glushchenko on 7/20/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Cocoa import MASShortcut import Foundation import Shout import UserNotifications import WebKit class ViewController: EditorViewController, NSSplitViewDelegate, NSOutlineViewDelegate, NSOutlineViewDataSource, NSTextFieldDelegate, UNUserNotificationCenterDelegate { // MARK: - Properties public var fsManager: FileSystemEventManager? public var projectSettingsViewController: ProjectSettingsViewController? private var isPreLoaded = false let storage = Storage.shared() private var sidebarTimer = Timer() private var selectRowTimer = Timer() private let searchQueue = OperationQueue() private let counterQueue = OperationQueue() public static var gitQueue = OperationQueue() public static var gitQueueBusy: Bool = false public static var gitQueueOperationDate: Date? public var prevCommit: Commit? /* Git */ private var updateViews = [Note]() var tagsScannerQueue = [Note]() @available(*, deprecated, message: "Remove after macOS 10.15 is no longer supported") public var printerLegacy: PrinterLegacy? override var representedObject: Any? { didSet { } // Update the view, if already loaded. } // MARK: - IBOutlets @IBOutlet weak var nonSelectedLabel: NSTextField! @IBOutlet weak var splitView: EditorSplitView! @IBOutlet var editor: EditTextView! @IBOutlet weak var editAreaScroll: EditorScrollView! @IBOutlet weak var search: SearchTextField! @IBOutlet weak var notesTableView: NotesTableView! @IBOutlet var noteMenu: NSMenu! @IBOutlet weak var sidebarOutlineView: SidebarOutlineView! @IBOutlet weak var sidebarSplitView: NSSplitView! @IBOutlet weak var notesListCustomView: NSView! @IBOutlet weak var outlineHeader: OutlineHeaderView! @IBOutlet weak var showInSidebar: NSMenuItem! @IBOutlet weak var searchTopConstraint: NSLayoutConstraint! @IBOutlet weak var lockedFolder: NSTextField! @IBOutlet weak var newNoteButton: NSButton! @IBOutlet weak var titleLabel: TitleTextField! { didSet { let clickGesture = NSClickGestureRecognizer() clickGesture.target = self clickGesture.numberOfClicksRequired = 2 clickGesture.buttonMask = 0x1 clickGesture.action = #selector(switchTitleToEditMode) titleLabel.addGestureRecognizer(clickGesture) } } @IBOutlet weak var shareButton: NSButton! @IBOutlet weak var sortByOutlet: NSMenuItem! @IBOutlet weak var titleBarAdditionalView: NSVisualEffectView! { didSet { let layer = CALayer() layer.frame = titleBarAdditionalView.bounds layer.backgroundColor = .clear titleBarAdditionalView.wantsLayer = true titleBarAdditionalView.layer = layer titleBarAdditionalView.alphaValue = 0 } } @IBOutlet weak var previewButton: NSButton! { didSet { previewButton.state = vcEditor?.isPreviewEnabled() == true ? .on : .off } } @IBOutlet weak var titleBarView: TitleBarView! { didSet { titleBarView.onMouseExitedClosure = { [weak self] in DispatchQueue.main.async { NSAnimationContext.runAnimationGroup({ context in context.duration = 0.35 self?.titleBarAdditionalView.alphaValue = 0 self?.titleLabel.backgroundColor = .clear }, completionHandler: nil) } } titleBarView.onMouseEnteredClosure = { [weak self] in DispatchQueue.main.async { guard self?.titleLabel.isEnabled == false || self?.titleLabel.isEditable == false else { return } if let note = self?.editor.note { if note.isEncryptedAndLocked() { self?.lockUnlock.image = NSImage(named: NSImage.lockLockedTemplateName) } else { self?.lockUnlock.image = NSImage(named: NSImage.lockUnlockedTemplateName) } } self?.lockUnlock.isHidden = (self?.editor.note == nil) NSAnimationContext.runAnimationGroup({ context in context.duration = 0.35 self?.titleBarAdditionalView.alphaValue = 1 }, completionHandler: nil) } } } } @IBOutlet weak var lockUnlock: NSButton! @IBOutlet weak var sidebarScrollView: NSScrollView! @IBOutlet weak var notesScrollView: NSScrollView! @IBOutlet weak var menuChangeCreationDate: NSMenuItem! @IBOutlet weak var counter: NSTextField! @IBOutlet weak var notesCounterViewHeight: NSLayoutConstraint! @IBOutlet weak var notesCounter: NSTextField! // MARK: - Overrides override func viewDidLoad() { if isPreLoaded { return } isPreLoaded = true if #available(macOS 12.0, *) { let image = NSImage(systemSymbolName: "square.and.pencil", accessibilityDescription: nil) var config = NSImage.SymbolConfiguration(textStyle: .body, scale: .large) config = config.applying(.init(paletteColors: [.systemTeal, .systemGray])) newNoteButton.image = image?.withSymbolConfiguration(config) } else { newNoteButton.image = NSImage(imageLiteralResourceName: "new_note_button").resize(to: CGSize(width: 20, height: 20)) } configureShortcuts() configureDelegates() configureLayout() configureEditor() // Must before event manager starts self.storage.checkWelcome() fsManager = FileSystemEventManager(storage: storage, delegate: self) fsManager?.start() loadBookmarks(data: UserDefaultsManagement.sftpAccessData) loadBookmarks(data: UserDefaultsManagement.gitPrivateKeyData) loadMoveMenu() loadSortBySetting() checkSidebarConstraint() #if CLOUD_RELATED_BLOCK registerKeyValueObserver() #endif ViewController.gitQueue.maxConcurrentOperationCount = 1 notesTableView.doubleAction = #selector(self.doubleClickOnNotesTable) DispatchQueue.global().async { self.storage.loadInboxAndTrash() DispatchQueue.main.async { self.buildSearchQuery() self.configureSidebar() self.configureNoteList() } } } override func viewDidAppear() { // Init window size if UserDefaultsManagement.isFirstLaunch { if let window = self.view.window { let newSize = NSSize(width: 1200, height: window.frame.height) window.setContentSize(newSize) window.center() } self.sidebarSplitView.setPosition(200, ofDividerAt: 0) self.splitView.setPosition(300, ofDividerAt: 0) UserDefaultsManagement.sidebarTableWidth = 200 UserDefaultsManagement.notesTableWidth = 300 UserDefaultsManagement.isFirstLaunch = false } // Restore window position if let x = UserDefaultsManagement.lastScreenX, let y = UserDefaultsManagement.lastScreenY { view.window?.setFrameOrigin(NSPoint(x: x, y: y)) UserDefaultsManagement.lastScreenX = nil UserDefaultsManagement.lastScreenY = nil } if UserDefaultsManagement.fullScreen { view.window?.toggleFullScreen(nil) } } public func preLoadProjectsData() { let projectsLoading = Date() let results = self.storage.getProjectDiffs() OperationQueue.main.addOperation { self.sidebarOutlineView.removeRows(projects: results.0) self.sidebarOutlineView.insertRows(projects: results.1) self.notesTableView.doVisualChanges(results: (results.2, results.3, [])) } print("0. Projects diff loading finished in \(projectsLoading.timeIntervalSinceNow * -1) seconds") let diffLoading = Date() for project in self.storage.getProjects() { let changes = project.checkNotesCacheDiff() self.notesTableView.doVisualChanges(results: changes) } // Reload added projects self.fsManager?.restart() self.storage.migrationAPIIds() print("1. Notes diff loading finished in \(diffLoading.timeIntervalSinceNow * -1) seconds") let tagsPoint = Date() // Schedule git actions self.scheduleSnapshots() self.schedulePull() // Loads tags self.storage.loadNotesContent() DispatchQueue.main.async { if self.storage.isCrashedLastTime && !UserDefaultsManagement.showWelcome { // Unsafe – resets selected note self.restoreSidebar() } UserDefaultsManagement.showWelcome = false // Safe – only tags loading self.sidebarOutlineView.loadAllTags() } print("2. Tags loading finished in \(tagsPoint.timeIntervalSinceNow * -1) seconds") let highlightCachePoint = Date() for note in self.storage.noteList { note.cache() } print("3. Notes attributes cache for \(self.storage.noteList.count) notes in \(highlightCachePoint.timeIntervalSinceNow * -1) seconds") let gitCachePoint = Date() self.cacheGitRepositories() print("4. git history cached in \(gitCachePoint.timeIntervalSinceNow * -1) seconds") } // MARK: - Initial configuration private func configureLayout() { dropTitle() editor.configure() notesTableView.setDraggingSourceOperationMask(.every, forLocal: false) if (UserDefaultsManagement.horizontalOrientation) { self.splitView.isVertical = false notesCounterViewHeight.constant = 0 notesCounter.isHidden = true } self.menuChangeCreationDate.title = NSLocalizedString("Change Creation Date", comment: "Menu") self.shareButton.sendAction(on: .leftMouseDown) self.setTableRowHeight() self.sidebarOutlineView.sidebarItems = Sidebar().getList() self.sidebarOutlineView.reloadData() sidebarOutlineView.selectionHighlightStyle = .regular sidebarOutlineView.backgroundColor = .windowBackgroundColor self.sidebarSplitView.autosaveName = "SidebarSplitView" self.splitView.autosaveName = "EditorSplitView" // Always show notes list at launch if (self.splitView.subviews[0].frame.width < 10) { self.splitView.setPosition(300, ofDividerAt: 0) } notesScrollView.scrollerStyle = .overlay sidebarScrollView.scrollerStyle = .overlay if let cell = search.cell as? NSSearchFieldCell { cell.searchButtonCell?.target = self cell.searchButtonCell?.action = #selector(openRecentPopup(_:)) } DistributedNotificationCenter.default().addObserver(self, selector: #selector(onWakeNote(note:)), name: Notification.Name("com.apple.screenIsUnlocked"), object: nil) NSWorkspace.shared.notificationCenter.addObserver( self, selector: #selector(onSleepNote(note:)), name: NSWorkspace.willSleepNotification, object: nil) NSWorkspace.shared.notificationCenter.addObserver( self, selector: #selector(onUserSwitch(note:)), name: NSWorkspace.sessionDidBecomeActiveNotification, object: nil) DistributedNotificationCenter.default().addObserver( self, selector: #selector(onScreenLocked(note:)), name: NSNotification.Name(rawValue: "com.apple.screenIsLocked"), object: nil ) DistributedNotificationCenter.default().addObserver( self, selector: #selector(onAccentColorChanged(note:)), name: NSNotification.Name(rawValue: "AppleColorPreferencesChangedNotification"), object: nil ) DistributedNotificationCenter.default.addObserver( self, selector: #selector(onAccentColorChanged(note:)), name: NSNotification.Name(rawValue: "AppleInterfaceThemeChangedNotification"), object: nil ) } public func restoreSidebar() { self.sidebarOutlineView.sidebarItems = Sidebar().getList() self.sidebarOutlineView.reloadData() self.storage.restoreProjectsExpandState() for project in self.storage.getProjects() { if project.isExpanded { self.sidebarOutlineView.expandItem(project) } } } public func configureSidebar() { if isVisibleSidebar() { self.restoreSidebar() if UserDefaultsManagement.lastSidebarItem != nil || UserDefaultsManagement.lastProjectURL != nil || Storage.shared().welcomeProject != nil { if let welcome = Storage.shared().welcomeProject { let item = self.sidebarOutlineView.row(forItem: welcome) if item > -1 { self.sidebarOutlineView.selectRowIndexes([item], byExtendingSelection: false) } } else if let lastSidebarItem = UserDefaultsManagement.lastSidebarItem { let sidebarItem = self.sidebarOutlineView.sidebarItems?.first(where: { ($0 as? SidebarItem)?.type.rawValue == lastSidebarItem }) let item = self.sidebarOutlineView.row(forItem: sidebarItem) if item > -1 { self.sidebarOutlineView.selectRowIndexes([item], byExtendingSelection: false) } } else if let lastURL = UserDefaultsManagement.lastProjectURL, let project = self.storage.getProjectBy(url: lastURL) { let item = self.sidebarOutlineView.row(forItem: project) if item > -1 { self.sidebarOutlineView.selectRowIndexes([item], byExtendingSelection: false) } } } } } private func configureNoteList() { updateTable() { DispatchQueue.main.async { // Init first selected note for welcome if let note = Storage.shared().welcomeNote { note.previewState = true self.notesTableView.select(note: note) Storage.shared().welcomeNote = nil } self.restoreOpenedWindows() self.importAndCreate() DispatchQueue.global().async { self.preLoadProjectsData() } } } } private func configureEditor() { self.editor?.linkTextAttributes = [ .foregroundColor: NSColor.init(named: "link")! ] self.editor.usesFindBar = true self.editor.isIncrementalSearchingEnabled = true editor.initTextStorage() editor.editorViewController = self self.editor.viewDelegate = self // configure editor view controller vcEditor = editor vcTitleLabel = titleLabel vcEditorScrollView = editAreaScroll vcNonSelectedLabel = nonSelectedLabel super.initView() } private func configureShortcuts() { MASShortcutMonitor.shared().register(UserDefaultsManagement.newNoteShortcut, withAction: { self.makeNoteShortcut() }) MASShortcutMonitor.shared().register(UserDefaultsManagement.searchNoteShortcut, withAction: { self.searchShortcut() }) MASShortcutMonitor.shared().register(UserDefaultsManagement.quickNoteShortcut, withAction: { self.quickNote(self) }) MASShortcutMonitor.shared().register(UserDefaultsManagement.activateShortcut, withAction: { self.searchShortcut(activate: true) }) NSEvent.addLocalMonitorForEvents(matching: NSEvent.EventTypeMask.flagsChanged) { return $0 } NSEvent.addLocalMonitorForEvents(matching: NSEvent.EventTypeMask.keyDown) { if self.keyDown(with: $0) { return $0 } return nil } } private func configureDelegates() { self.search.vcDelegate = self self.search.delegate = self.search self.sidebarSplitView.delegate = self self.sidebarOutlineView.viewDelegate = self if #available(macOS 10.14, *) { UNUserNotificationCenter.current().delegate = self } } // MARK: - Actions @IBAction public func openRecentPopup(_ sender: Any) { search.searchesMenu = search.generateRecentMenu() let general = search.searchesMenu!.item(at: 0) search.searchesMenu!.popUp(positioning: general, at: NSPoint(x: 5, y: search.frame.height + 7), in: search) } @IBAction func searchAndCreate(_ sender: Any) { AppDelegate.mainWindowController?.window?.makeKeyAndOrderFront(nil) guard let vc = ViewController.shared() else { return } if let view = NSApplication.shared.mainWindow?.firstResponder as? NSTextView, let textField = view.superview?.superview { if textField.isKind(of: SearchTextField.self) { if vc.search.searchesMenu != nil { vc.search.searchesMenu = nil } else { vc.search.searchesMenu = vc.search.generateRecentMenu() let general = vc.search.searchesMenu!.item(at: 0) vc.search.searchesMenu!.popUp(positioning: general, at: NSPoint(x: 5, y: vc.search.frame.height + 7), in: vc.search) return } } } let size = UserDefaultsManagement.horizontalOrientation ? vc.splitView.subviews[0].frame.height : vc.splitView.subviews[0].frame.width if size == 0 { toggleNoteList(self) } vc.search.window?.makeFirstResponder(vc.search) } @IBAction func sortBy(_ sender: NSMenuItem) { if let id = sender.identifier { let key = String(id.rawValue.dropFirst(3)) guard let sortBy = SortBy(rawValue: key) else { return } if sortBy.rawValue == UserDefaultsManagement.sort.rawValue { UserDefaultsManagement.sortDirection = !UserDefaultsManagement.sortDirection } UserDefaultsManagement.sort = sortBy if let submenu = sortByOutlet.submenu { for item in submenu.items { item.state = NSControl.StateValue.off } } sender.state = NSControl.StateValue.on ViewController.shared()?.buildSearchQuery() ViewController.shared()?.updateTable() } } // Ask project password before move to encrypted public func moveReq(notes: [Note], project: Project, completion: @escaping (Bool) -> ()) { for note in notes { if note.isEncrypted() && project.isEncrypted { let alert = NSAlert() alert.alertStyle = .critical alert.informativeText = NSLocalizedString("You cannot move an already encrypted note to an encrypted directory. You must first decrypt the note and repeat the steps.", comment: "") alert.messageText = NSLocalizedString("Move error", comment: "") alert.runModal() return } } // Encrypted and locked if project.isEncrypted && project.isLocked() { getMasterPassword() { password in self.sidebarOutlineView.unlock(projects: [project], password: password) if project.password != nil { DispatchQueue.main.async { self.move(notes: notes, project: project) for note in notes { note.encryptAndUnlock(password: password) } completion(true) } return } completion(false) } return } self.move(notes: notes, project: project) // Encrypted and non locked if project.isEncrypted, let password = project.password { for note in notes { note.encryptAndUnlock(password: password) } } completion(true) } private func move(notes: [Note], project: Project) { let selectedRow = notesTableView.selectedRowIndexes.min() for note in notes { if note.project == project { continue } if note.isEncrypted() { _ = note.lock() } let destination = project.url.appendingPathComponent(note.name, isDirectory: false) note.moveImages(to: project) _ = note.move(to: destination, project: project) if !storage.searchQuery.isFit(note: note) { notesTableView.removeRows(notes: [note]) if let i = selectedRow, i > -1 { if notesTableView.countNotes() > i { notesTableView.selectRow(i) } else { notesTableView.selectRow(notesTableView.countNotes() - 1) } } } note.invalidateCache() } editor.clear() } override func viewDidResize() { guard let vc = ViewController.shared() else { return } vc.checkSidebarConstraint() super.viewDidResize() } func reloadSideBar() { guard let outline = sidebarOutlineView else { return } sidebarTimer.invalidate() sidebarTimer = Timer.scheduledTimer(timeInterval: 1.2, target: outline, selector: #selector(outline.reloadSidebar), userInfo: nil, repeats: false) } func setTableRowHeight() { notesTableView.rowHeight = CGFloat(21 + UserDefaultsManagement.cellSpacing) notesTableView.reloadData() } public func keyDown(with event: NSEvent) -> Bool { guard let mw = MainWindowController.shared() else { return false } guard self.alert == nil else { if event.keyCode == kVK_Escape, let unwrapped = alert { mw.endSheet(unwrapped.window) self.alert = nil } return true } if event.modifierFlags.contains(.shift) && event.modifierFlags.contains(.option) && event.keyCode == kVK_ANSI_N { createFolder(NSMenuItem()) return false } // Return / Cmd + Return navigation if event.keyCode == kVK_Return { if let fr = NSApp.mainWindow?.firstResponder, self.alert == nil { if event.modifierFlags.contains(.command) { if fr.isKind(of: NotesTableView.self) { NSApp.mainWindow?.makeFirstResponder(self.sidebarOutlineView) if sidebarOutlineView.selectedRowIndexes.count == 0 { sidebarOutlineView.selectRowIndexes([0], byExtendingSelection: false) } else { sidebarOutlineView.selectRowIndexes(sidebarOutlineView.selectedRowIndexes, byExtendingSelection: false) } return false } if fr.isKind(of: EditTextView.self) || fr.isKind(of: MPreviewView.self) { NSApp.mainWindow?.makeFirstResponder(self.notesTableView) return false } } else { if fr.isKind(of: SidebarOutlineView.self) { self.notesTableView.selectCurrent() NSApp.mainWindow?.makeFirstResponder(self.notesTableView) return false } if let note = editor.note, fr.isKind(of: NotesTableView.self) { if note.container != .encryptedTextPack { if vcEditor?.isPreviewEnabled() == true { disablePreview() } NSApp.mainWindow?.makeFirstResponder(editor) } return false } } } return true } // Tab / Control + Tab if event.keyCode == kVK_Tab { if event.modifierFlags.contains(.control) { self.notesTableView.window?.makeFirstResponder(self.notesTableView) return true } if let fr = NSApp.mainWindow?.firstResponder, fr.isKind(of: NotesTableView.self) { NSApp.mainWindow?.makeFirstResponder(self.notesTableView) return false } } if event.keyCode == kVK_Escape && event.modifierFlags.contains(.option) { editor.forceSystemAutocomplete = true (view.window?.firstResponder as? NSTextView)?.complete(nil) return true } // Focus search bar on ESC if ( ( event.keyCode == kVK_Escape || ( event.characters == "." && event.modifierFlags.contains(.command) ) ) && NSApplication.shared.mainWindow == NSApplication.shared.keyWindow && UserDefaultsManagement.shouldFocusSearchOnESCKeyDown && !editor.hasMarkedText() ) { self.view.window?.orderFront(nil) self.view.window?.makeKey() search.searchesMenu = nil if NSApplication.shared.mainWindow?.firstResponder === editor, editor.selectedRange().length > 0 { editor.selectedRange = NSRange(location: editor.selectedRange().upperBound, length: 0) return false } if let view = NSApplication.shared.mainWindow?.firstResponder as? NSTextView, let textField = view.superview?.superview, textField.isKind(of: NameTextField.self) { NSApp.mainWindow?.makeFirstResponder( self.notesTableView) return false } if let mView = self.editor.markdownView, mView.isFindPanelVisible { mView.hideFindPanel() NSApp.mainWindow?.makeFirstResponder(mView.webView) return false } if self.editAreaScroll.isFindBarVisible { cancelTextSearch() NSApp.mainWindow?.makeFirstResponder(editor) return false } // Renaming is in progress if titleLabel.isEditable { titleLabel.editModeOff() titleLabel.window?.makeFirstResponder(notesTableView) return false } UserDefaultsManagement.lastSidebarItem = nil UserDefaultsManagement.lastProjectURL = nil UserDefaultsManagement.lastSelectedURL = nil notesTableView.scroll(.zero) let hasSelectedNotes = notesTableView.selectedRow > -1 let hasSelectedBarItem = sidebarOutlineView.selectedRow > -1 if hasSelectedBarItem && hasSelectedNotes { UserDataService.instance.isNotesTableEscape = true notesTableView.deselectAll(nil) NSApp.mainWindow?.makeFirstResponder(search) return false } sidebarOutlineView.deselectAll(nil) sidebarOutlineView.scrollRowToVisible(0) cleanSearchAndEditArea() return true } // Search cmd-f if (event.characters?.unicodeScalars.first == "f" && event.modifierFlags.contains(.command) && !event.modifierFlags.contains(.control)) { if self.notesTableView.getSelectedNote() != nil { if search.stringValue.count > 0 { let fullText = search.stringValue let startIndex = fullText.startIndex let range = search.selectedRange let selectionStart = fullText.index(startIndex, offsetBy: range.location) let textBefore = String(fullText[startIndex.. 0 else { sender.stringValue = note.getTitleWithoutLabel() return } sender.isEditable = false let newUrl = note.getNewURL(name: value) UserDataService.instance.focusOnImport = newUrl if note.url.path == newUrl.path { return } note.overwrite(url: newUrl) do { try FileManager.default.moveItem(at: url, to: newUrl) print("File moved from \"\(url.deletingPathExtension().lastPathComponent)\" to \"\(newUrl.deletingPathExtension().lastPathComponent)\"") } catch { note.overwrite(url: url) } } @IBAction func makeMenu(_ sender: Any) { guard let vc = ViewController.shared() else { return } if let type = vc.getSidebarType(), type == .Trash { vc.sidebarOutlineView.deselectAllRows() } _ = vc.createNote() } @IBAction func renameMenu(_ sender: Any) { guard let vc = ViewController.shared() else { return } vc.titleLabel.restoreResponder = vc.view.window?.firstResponder vc.switchTitleToEditMode() } @objc func switchTitleToEditMode() { guard let vc = ViewController.shared() else { return } if vc.notesTableView.selectedRow > -1 { vc.titleLabel.editModeOn() vc.titleBarAdditionalView.alphaValue = 0 if let note = vc.editor.note, note.getFileName().isValidUUID { vc.titleLabel.stringValue = note.getFileName() } return } if let md = AppDelegate.mainWindowController { if let actionOnDoubleClick = UserDefaults.standard.object(forKey: "AppleActionOnDoubleClick") as? String { switch actionOnDoubleClick { case "Maximize": md.maximizeWindow() case "Minimize": md.window?.performMiniaturize(nil) default: break } } } } @IBAction func toggleNoteList(_ sender: Any) { guard let vc = ViewController.shared() else { return } let size = UserDefaultsManagement.horizontalOrientation ? vc.splitView.subviews[0].frame.height : vc.splitView.subviews[0].frame.width if size == 0 { var size = UserDefaultsManagement.notesTableWidth if UserDefaultsManagement.notesTableWidth == 0 { size = 300 } vc.splitView.shouldHideDivider = false vc.splitView.setPosition(size, ofDividerAt: 0) } else if vc.splitView.shouldHideDivider { vc.splitView.shouldHideDivider = false vc.splitView.setPosition(UserDefaultsManagement.notesTableWidth, ofDividerAt: 0) } else { UserDefaultsManagement.notesTableWidth = size vc.splitView.shouldHideDivider = true vc.splitView.setPosition(0, ofDividerAt: 0) DispatchQueue.main.async { vc.splitView.setPosition(0, ofDividerAt: 0) } } vc.editor.updateTextContainerInset() } @IBAction func toggleSidebar(_ sender: Any) { guard let vc = ViewController.shared() else { return } if isVisibleSidebar() { UserDefaultsManagement.sidebarTableWidth = vc.sidebarSplitView.subviews[0].frame.width vc.sidebarSplitView.setPosition(0, ofDividerAt: 0) } else { vc.sidebarSplitView.setPosition(UserDefaultsManagement.sidebarTableWidth, ofDividerAt: 0) vc.reloadSideBar() } vc.editor.updateTextContainerInset() } @IBAction func emptyTrash(_ sender: NSMenuItem) { let notes = storage.getAllTrash() for note in notes { _ = note.removeFile() } NSSound(named: "Pop")?.play() } @IBAction func lockAll(_ sender: Any) { let projects = storage.getProjects().filter({ $0.isEncrypted && !$0.isLocked() }) sidebarOutlineView.lock(projects: projects) let editors = AppDelegate.getEditTextViews() var unlockedEditors = [EditTextView]() for editor in editors { if let note = editor.note, note.isUnlocked() { unlockedEditors.append(editor) } } for editor in unlockedEditors { editor.lockEncryptedView() } let notes = storage.noteList.filter({ $0.isUnlocked() }) for note in notes { if note.lock() { removeTags(note: note) notesTableView.reloadRow(note: note) } } if let window = notesTableView.window, window == view.window { window.makeFirstResponder(notesTableView) } } @available(macOS 10.14, *) public func sendNotification() { let center = UNUserNotificationCenter.current() center.delegate = self center.requestAuthorization(options: [.badge,.sound,.alert]) { granted, error in if error != nil { print("User permission is not granted : \(granted)") } } let content = UNMutableNotificationContent() content.title = "Upload over SSH done" content.sound = .default let date = Date().addingTimeInterval(1) let dateComponent = Calendar.current.dateComponents([.year,.month,.day,.hour,.minute,.second], from: date) let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponent, repeats: false) let uuid = UUID().uuidString let request = UNNotificationRequest(identifier: uuid, content: content, trigger: trigger) center.add(request) { error in } } func controlTextDidEndEditing(_ obj: Notification) { guard let textField = obj.object as? NSTextField, textField == titleLabel else { return } if titleLabel.isEditable == true { titleLabel.editModeOff() fileName(titleLabel) view.window?.makeFirstResponder(notesTableView) } else { if let currentNote = notesTableView.getSelectedNote() { updateTitle(note: currentNote) } } } public func reSort(note: Note) { if !updateViews.contains(note) { updateViews.append(note) } rowUpdaterTimer.invalidate() rowUpdaterTimer = Timer.scheduledTimer(timeInterval: 1.2, target: self, selector: #selector(updateTableViews), userInfo: nil, repeats: false) } public func removeForever() { guard let vc = ViewController.shared() else { return } guard let notes = vc.notesTableView.getSelectedNotes() else { return } guard let window = MainWindowController.shared() else { return } vc.alert = NSAlert() guard let alert = vc.alert else { return } alert.messageText = String(format: NSLocalizedString("Are you sure you want to irretrievably delete %d note(s)?", comment: ""), notes.count) alert.informativeText = NSLocalizedString("This action cannot be undone.", comment: "") alert.addButton(withTitle: NSLocalizedString("Remove note(s)", comment: "")) alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) alert.beginSheetModal(for: window) { (returnCode: NSApplication.ModalResponse) -> Void in if returnCode == NSApplication.ModalResponse.alertFirstButtonReturn { let selectedRow = vc.notesTableView.selectedRowIndexes.min() vc.editor.clear() vc.storage.removeNotes(notes: notes, completely: true) { _ in DispatchQueue.main.async { vc.notesTableView.removeRows(notes: notes) if let i = selectedRow, i > -1 { vc.notesTableView.selectRow(i) } } } } vc.alert = nil } } @objc private func updateTableViews() { let editors = AppDelegate.getEditTextViews() notesTableView.beginUpdates() for note in updateViews { notesTableView.reloadRow(note: note) if search.stringValue.count == 0 { sortAndMove(note: note) } // Reloading nstextview in multiple windows for editor in editors { if let window = editor.window, let editorNote = editor.note, editorNote == note { if editor.viewDelegate != nil { // Main window self.updateCounters(note: editorNote) } if !editor.isLastEdited, !window.isKeyWindow { editor.editorViewController?.refillEditArea(force: true) } } } } updateViews.removeAll() notesTableView.endUpdates() } public func updateCounters(note: Note? = nil, charRange: NSRange? = nil) { guard let note = note else { self.counter.stringValue = String() return } counterQueue.cancelAllOperations() let operation = BlockOperation() operation.addExecutionBlock { [weak self] in var title = String() if let charRange = charRange, charRange.length > 0 { if let string = note.content.string.substring(nsRange: charRange) { title = "W: \(string.countWords()) | C: \(string.countChars())" } } else { title = "W: \(note.content.string.countWords()) | C: \(note.content.string.countChars())" } if operation.isCancelled { return } DispatchQueue.main.async { self?.counter.stringValue = title } } counterQueue.addOperation(operation) } public func updateNotesCounter() { var i = 0 if notesTableView.selectedRowIndexes.count > 0 { i = notesTableView.selectedRowIndexes.count } else { i = notesTableView.countNotes() } notesCounter.stringValue = "N: \(i)" } func getSidebarType() -> SidebarItemType? { let sidebarItem = sidebarOutlineView.item(atRow: sidebarOutlineView.selectedRow) as? SidebarItem if let type = sidebarItem?.type { return type } return nil } public func getSidebarItem() -> SidebarItem? { if let sidebarItem = sidebarOutlineView.item(atRow: sidebarOutlineView.selectedRow) as? SidebarItem { return sidebarItem } return nil } func updateTable(completion: @escaping () -> Void = {}) { let timestamp = Date().toMillis() self.search.timestamp = timestamp self.searchQueue.cancelAllOperations() let operation = BlockOperation() operation.addExecutionBlock { [weak self] in guard let self = self else {return} let projects = Storage.shared().searchQuery.projects for project in projects { self.preLoadNoteTitles(in: project) } let source = self.storage.noteList var notes = [Note]() for note in source { if operation.isCancelled { completion() return } if self.storage.searchQuery.isFit(note: note) { notes.append(note) } } let orderedNotesList = self.storage.sortNotes(noteList: notes, operation: operation) if orderedNotesList == self.notesTableView.getNoteList() { // important for cleanSearchAndEditArea func call completion() return } if operation.isCancelled { return } guard orderedNotesList.count > 0 else { DispatchQueue.main.async { self.editor.clear() self.notesTableView.setNoteList(notes: orderedNotesList) self.notesTableView.reloadData() self.updateNotesCounter() completion() } return } DispatchQueue.main.async { self.notesTableView.setNoteList(notes: orderedNotesList) self.notesTableView.reloadData() self.updateNotesCounter() completion() } } self.searchQueue.addOperation(operation) } /* Load titles in cases sort by Title */ private func preLoadNoteTitles(in project: Project) { if (UserDefaultsManagement.sort == .title || project.settings.sortBy == .title) && project.settings.isFirstLineAsTitle() { let notes = storage.noteList.filter({ $0.project == project }) for note in notes { if !note.isLoaded { note.load() } note.loadPreviewInfo() } } } public func reloadFonts() { let webkitPreview = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("wkPreview") try? FileManager.default.removeItem(at: webkitPreview) Storage.shared().resetCacheAttributes() let editors = AppDelegate.getEditTextViews() for editor in editors { if let evc = editor.editorViewController { MPreviewView.template = nil NotesTextProcessor.resetCaches() evc.refillEditArea(force: true) } } } public func buildSearchQuery() { let searchQuery = SearchQuery() var projects = [Project]() var tags = [String]() var type: SidebarItemType? if let sidebarProjects = sidebarOutlineView.getSidebarProjects() { projects = sidebarProjects } // Iniot welcome project if let project = Storage.shared().welcomeProject { projects = [project] } if let sidebarTags = sidebarOutlineView.getSidebarTags() { tags = sidebarTags let currentModifiers = NSEvent.modifierFlags let isCommandPressed = currentModifiers.contains(.command) let isShiftPressed = currentModifiers.contains(.shift) if isCommandPressed && isShiftPressed { searchQuery.tagsModifierAnd(true) } } if let sidebarTableView = self.sidebarOutlineView { let indexPaths = sidebarTableView.selectedRowIndexes for indexPath in indexPaths { if let item = sidebarTableView.item(atRow: indexPath) as? SidebarItem { if item.type == .All || item.type == .Untagged || item.type == .Todo || item.type == .Trash || item.type == .Inbox { type = item.type } } } } if projects.count == 0 && type == nil { type = .All } let filter = search.stringValue searchQuery.projects = projects searchQuery.tags = tags searchQuery.setFilter(filter) if let type = type { searchQuery.setType(type) } self.storage.setSearchQuery(value: searchQuery) } @objc func selectNullTableRow(note: Note) { self.selectRowTimer.invalidate() self.selectRowTimer = Timer.scheduledTimer(timeInterval: TimeInterval(0.2), target: self, selector: #selector(self.selectRowInstant), userInfo: note, repeats: false) } @objc private func selectRowInstant(_ timer: Timer) { if let note = timer.userInfo as? Note { if let i = self.notesTableView.getIndex(for: note) { notesTableView.selectRowIndexes([i], byExtendingSelection: false) notesTableView.scrollRowToVisible(i) } } } func focusTable() { let index = self.notesTableView.selectedRow > -1 ? self.notesTableView.selectedRow : 0 self.notesTableView.window?.makeFirstResponder(self.notesTableView) self.notesTableView.selectRowIndexes([index], byExtendingSelection: false) self.notesTableView.scrollRowToVisible(index) } func cleanSearchAndEditArea(shouldBecomeFirstResponder: Bool = true, completion: (() -> ())? = nil) { search.stringValue = "" search.lastSearchQuery = "" if shouldBecomeFirstResponder { search.becomeFirstResponder() } notesTableView.selectRowIndexes(IndexSet(), byExtendingSelection: false) editor.clear() updateCounters(note: nil) self.buildSearchQuery() self.updateTable() { DispatchQueue.main.async { if shouldBecomeFirstResponder { self.sidebarOutlineView.reloadTags() } if let completion = completion { completion() return } } } } func makeNoteShortcut() { let clipboard = NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.string) if let clipboard = clipboard { _ = createNote(content: clipboard) UNUserNotificationCenter.current().getNotificationSettings { settings in guard settings.authorizationStatus == .notDetermined else { return } UNUserNotificationCenter.current().requestAuthorization( options: [.alert, .sound] ) { _, _ in } } let content = UNMutableNotificationContent() content.title = NSLocalizedString("Clipboard successfully saved", comment: "") content.body = clipboard content.sound = .default UNUserNotificationCenter.current().add( UNNotificationRequest( identifier: UUID().uuidString, content: content, trigger: nil )) } } func searchShortcut(activate: Bool = false) { guard let mainWindow = MainWindowController.shared() else { return } if ( NSApplication.shared.isActive && !NSApplication.shared.isHidden && !mainWindow.isMiniaturized ) { NSApplication.shared.hide(nil) return } UserDefaultsManagement.lastScreenX = nil UserDefaultsManagement.lastScreenY = nil NSApp.activate(ignoringOtherApps: true) mainWindow.makeKeyAndOrderFront(self) guard let controller = mainWindow.contentViewController as? ViewController else { return } if !activate { mainWindow.makeFirstResponder(controller.search) } } public func sortAndMove(note: Note, project: Project? = nil) { guard let srcIndex = notesTableView.getIndex(for: note) else { return } let notes = notesTableView.getNoteList() let resorted = storage.sortNotes(noteList: notes) guard let dstIndex = resorted.firstIndex(of: note) else { return } if srcIndex != dstIndex { notesTableView.moveRow(at: srcIndex, to: dstIndex) notesTableView.setNoteList(notes: resorted) } } func pin(selectedNotes: [Note], toggle: Bool = false) { if selectedNotes.count == 0 { return } var state = notesTableView.getNoteList() var updatedNotes = [(Int, Note)]() for selectedNote in selectedNotes { guard let atRow = notesTableView.getIndex(for: selectedNote), let rowView = notesTableView.rowView(atRow: atRow, makeIfNecessary: false) as? NoteRowView, let cell = rowView.view(atColumn: 0) as? NoteCellView else { continue } updatedNotes.append((atRow, selectedNote)) if toggle { selectedNote.togglePin() } cell.renderPin() } let resorted = storage.sortNotes(noteList: notesTableView.getNoteList()) notesTableView.beginUpdates() let nowPinned = updatedNotes.filter { _, note in note.isPinned } for (row, note) in nowPinned { guard let newRow = resorted.firstIndex(where: { $0 === note }) else { continue } notesTableView.moveRow(at: row, to: newRow) let toMove = state.remove(at: row) state.insert(toMove, at: newRow) } let nowUnpinned = updatedNotes .filter({ (_, note) -> Bool in !note.isPinned }) .compactMap({ (_, note) -> (Int, Note)? in guard let curRow = state.firstIndex(where: { $0 === note }) else { return nil } return (curRow, note) }) for (row, note) in nowUnpinned.reversed() { guard let newRow = resorted.firstIndex(where: { $0 === note }) else { continue } notesTableView.moveRow(at: row, to: newRow) let toMove = state.remove(at: row) state.insert(toMove, at: newRow) } notesTableView.setNoteList(notes: resorted) notesTableView.endUpdates() //notesTableView.reloadData() //notesTableView.selectRowIndexes(newIndexes, byExtendingSelection: false) } func external(selectedNotes: [Note]) { if selectedNotes.count == 0 { return } for note in selectedNotes { var path = note.url.path if note.isTextBundle() && !note.isUnlocked(), let url = note.getContentFileURL() { path = url.path } NSWorkspace.shared.openFile(path, withApplication: UserDefaultsManagement.externalEditor) } } private func loadBookmarks(data: Data?) { if let accessData = data, let bookmarks = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSDictionary.self, NSURL.self, NSData.self], from: accessData) as? [URL: Data] { for bookmark in bookmarks { var isStale = false do { let url = try URL.init(resolvingBookmarkData: bookmark.value, options: NSURL.BookmarkResolutionOptions.withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale) if !url.startAccessingSecurityScopedResource() { print("RSA key not available: \(url.path)") } else { print("Access for RSA key is successfull restored \(url)") } } catch { print("Error restoring sftp bookmark: \(error)") } } } } func loadSortBySetting() { let viewLabel = NSLocalizedString("View", comment: "Menu") let sortByLabel = NSLocalizedString("Sort by", comment: "View menu") guard let menu = NSApp.menu, let view = menu.item(withTitle: viewLabel), let submenu = view.submenu, let sortMenu = submenu.item(withTitle: sortByLabel), let sortItems = sortMenu.submenu else { return } let sort = UserDefaultsManagement.sort for item in sortItems.items { if let id = item.identifier, id.rawValue == "SB.\(sort.rawValue)" { item.state = NSControl.StateValue.on } } } func registerKeyValueObserver() { NotificationCenter.default.addObserver(self, selector: #selector(ubiquitousKeyValueStoreDidChange(_:)), name: NSUbiquitousKeyValueStore.didChangeExternallyNotification, object: NSUbiquitousKeyValueStore.default) if NSUbiquitousKeyValueStore.default.synchronize() == false { fatalError("This app was not built with the proper entitlement requests.") } NSUbiquitousKeyValueStore.default.synchronize() } @objc func ubiquitousKeyValueStoreDidChange(_ notification: NSNotification) { if let keys = notification.userInfo?[NSUbiquitousKeyValueStoreChangedKeysKey] as? [String] { for key in keys { if key == "co.fluder.fsnotes.pins.shared" { let result = storage.restoreCloudPins() DispatchQueue.main.async { if let added = result.added { ViewController.shared()?.pin(selectedNotes: added) } if let removed = result.removed { ViewController.shared()?.pin(selectedNotes: removed) } } } if key.startsWith(string: "es.fsnot.project-settings") { let settingsKey = key.replacingOccurrences(of: "es.fsnot.project-settings", with: "") if let project = storage.getProjectBy(settingsKey: settingsKey) { project.reloadSettings() DispatchQueue.main.async { if let result = project.loadWebAPI() { let toReload = result.0 + result.1 for note in toReload { ViewController.shared()?.notesTableView.reloadRow(note: note) } } } } } } } } func checkSidebarConstraint() { if sidebarSplitView.subviews[0].frame.width > 50 { searchTopConstraint.constant = 8 return } if UserDefaultsManagement.hideSidebarTable || sidebarSplitView.subviews[0].frame.width < 50 { searchTopConstraint.constant = CGFloat(25) return } searchTopConstraint.constant = 8 } @IBAction func sidebarItemVisibility(_ sender: NSMenuItem) { sender.state = sender.state == .on ? .off : .on let isChecked = sender.state == .on switch sender.tag { case 1: UserDefaultsManagement.sidebarVisibilityInbox = isChecked case 2: UserDefaultsManagement.sidebarVisibilityNotes = isChecked case 3: UserDefaultsManagement.sidebarVisibilityTodo = isChecked case 5: UserDefaultsManagement.sidebarVisibilityTrash = isChecked case 6: UserDefaultsManagement.sidebarVisibilityUntagged = isChecked default: break } ViewController.shared()?.sidebarOutlineView.reloadSidebar() } @IBAction func prevHistory(_ sender: NSMenuItem) { guard let vc = ViewController.shared() else { return } if vc.notesTableView.historyPosition > 0 { let prev = vc.notesTableView.historyPosition - 1 let prevUrl = vc.notesTableView.history[prev] if let note = Storage.shared().getBy(url: prevUrl) { vc.notesTableView.saveNavigationHistory(note: note) vc.cleanSearchAndEditArea(completion: { () -> Void in vc.notesTableView.selectRowAndSidebarItem(note: note) }) } vc.notesTableView.historyPosition = prev } } @IBAction func nextHistory(_ sender: NSMenuItem) { guard let vc = ViewController.shared() else { return } if vc.notesTableView.historyPosition < vc.notesTableView.history.count - 1 { let next = vc.notesTableView.historyPosition + 1 let nextUrl = vc.notesTableView.history[next] if let note = Storage.shared().getBy(url: nextUrl) { vc.cleanSearchAndEditArea(completion: { () -> Void in vc.notesTableView.selectRowAndSidebarItem(note: note) }) } vc.notesTableView.historyPosition = next } } func textView(_ view: NSTextView, menu: NSMenu, for event: NSEvent, at charIndex: Int) -> NSMenu? { for item in menu.items { if item.title == NSLocalizedString("Copy Link", comment: "") { item.action = #selector(NSText.copy(_:)) } if item.title == NSLocalizedString("Font", comment: "") || item.title == "Make Link" || item.title == NSLocalizedString("Make Link", comment: "") { menu.removeItem(item) } } return menu } func splitViewWillResizeSubviews(_ notification: Notification) { editor.updateTextContainerInset() } public static func shared() -> ViewController? { return AppDelegate.mainWindowController?.window?.contentViewController as? ViewController } public func copy(project: Project, url: URL) -> URL { let fileName = url.lastPathComponent let destination = project.url.appendingPathComponent(fileName) do { try FileManager.default.copyItem(at: url, to: destination) return destination } catch { let dst = NameHelper.generateCopy(file: url, dstDir: project.url) try? FileManager.default.copyItem(at: url, to: dst) return dst } } @objc func onSleepNote(note: NSNotification) { if UserDefaultsManagement.lockOnSleep { lockAll(self) } } @objc func onScreenLocked(note: NSNotification) { if UserDefaultsManagement.lockOnScreenActivated{ lockAll(self) } } @objc func onAccentColorChanged(note: NSNotification) { sidebarOutlineView.reloadSidebar() } @objc func onUserSwitch(note: NSNotification) { if UserDefaultsManagement.lockOnUserSwitch { lockAll(self) } } override func restoreUserActivityState(_ userActivity: NSUserActivity) { guard let name = userActivity.userInfo?["note-file-name"] as? String, let state = userActivity.userInfo?["state"] as? String, let note = Storage.shared().getBy(name: name) else { return } vcEditor?.changePreviewState(state == "preview") note.previewState = state == "preview" notesTableView.selectRowAndSidebarItem(note: note) } /* Needs update UserActivity if selection did change */ func textViewDidChangeSelection(_ notification: Notification) { guard let textView = notification.object as? NSTextView else { return } if textView.window?.firstResponder == textView { let range = editor.selectedRange() if let editor = self.editor, let note = editor.note { self.updateCounters(note: note, charRange: range) } // Save position editor.note?.setSelectedRange(range: textView.selectedRange()) } editor.userActivity?.needsSave = true } @objc func doubleClickOnNotesTable() { let selected = notesTableView.clickedRow if (selected < 0) { return } if let note = notesTableView.getNote(at: selected) { openInNewWindow(note: note) } } public func restoreOpenedWindows() { guard let documentDir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { return } let projectsDataUrl = documentDir.appendingPathComponent("editors.settings") guard let data = try? Data(contentsOf: projectsDataUrl) else { return } guard let unarchivedData = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, NSDictionary.self, NSString.self, NSData.self, NSNumber.self, NSURL.self], from: data) as? [[String: Any]] else { return } var mainKey = false for item in unarchivedData.reversed() { guard let url = item["url"] as? URL, let frameData = item["frame"] as? Data, let main = item["main"] as? Bool, let isKeyWindow = item["key"] as? Bool, let preview = item["preview"] as? Bool, let note = self.storage.getBy(url: url) else { continue } if main { if isKeyWindow { mainKey = true } editor.changePreviewState(preview) if let i = self.notesTableView.getIndex(for: note) { note.previewState = self.editor.isPreviewEnabled() self.notesTableView.saveNavigationHistory(note: note) self.notesTableView.selectRow(i) self.notesTableView.scrollRowToVisible(i) self.editor.window?.makeFirstResponder(self.editor) } } else { guard let frame = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSValue.self, from: frameData)?.rectValue else { continue } self.openInNewWindow(note: note, frame: frame, preview: preview) } } if mainKey { NSApp.activate(ignoringOtherApps: true) self.view.window?.makeKeyAndOrderFront(self) } } // Important call after initial updateTable public func importAndCreate() { if let appDelegate = NSApplication.shared.delegate as? AppDelegate { // fsnotes://find if let url = appDelegate.url { appDelegate.url = nil appDelegate.search(url: url) return } // Open files in the app if let urls = appDelegate.urls { appDelegate.importNotes(urls: urls) return } // fsnotes://new/?title=URI-title&txt=URI-content let name = appDelegate.newName let content = appDelegate.newContent if nil != name || nil != content { if let note = self.createNote(name: name ?? "", content: content ?? "", openInNewWindow: appDelegate.newWindow), appDelegate.newWindow { openInNewWindow(note: note) } } } } public func isVisibleNoteList() -> Bool { guard let vc = ViewController.shared() else { return false } let size = UserDefaultsManagement.horizontalOrientation ? vc.splitView.subviews[0].frame.height : vc.splitView.subviews[0].frame.width if size == 0 || vc.splitView.shouldHideDivider { return false } return true } public func isVisibleSidebar() -> Bool { guard let vc = ViewController.shared() else { return false } let size = Int(vc.sidebarSplitView.subviews[0].frame.width) return size != 0 } private func cacheGitRepositories() { _ = Storage.shared().getProjects().filter({ $0.hasRepository() }).map({ $0.cacheHistory() }) } } ================================================ FILE: FSNotes/modern.icon/icon.json ================================================ { "fill" : "automatic", "groups" : [ { "layers" : [ { "fill-specializations" : [ { "value" : { "automatic-gradient" : "srgb:0.11674,0.11137,0.12082,1.00000" } }, { "appearance" : "dark", "value" : { "automatic-gradient" : "srgb:0.85953,0.87116,0.86212,1.00000" } } ], "image-name" : "Untitled-5.svg", "name" : "row1", "position" : { "scale" : 1, "translation-in-points" : [ -76.92312500000003, -199.8671875 ] } }, { "fill-specializations" : [ { "value" : { "automatic-gradient" : "srgb:0.11674,0.11137,0.12082,1.00000" } }, { "appearance" : "dark", "value" : { "automatic-gradient" : "srgb:0.85953,0.87116,0.86212,1.00000" } } ], "image-name" : "Untitled-2 3.svg", "name" : "row2", "position" : { "scale" : 1, "translation-in-points" : [ -136.953125, -51.15625 ] } }, { "fill-specializations" : [ { "value" : { "automatic-gradient" : "srgb:0.11674,0.11137,0.12082,1.00000" } }, { "appearance" : "dark", "value" : { "automatic-gradient" : "srgb:0.85953,0.87116,0.86212,1.00000" } } ], "image-name" : "Untitled-3 2.svg", "name" : "row3", "position" : { "scale" : 1, "translation-in-points" : [ -199.593125, 98.390625 ] } }, { "fill" : { "solid" : "display-p3:0.02353,0.56863,1.00000,1.00000" }, "image-name" : "Untitled-3 2.svg", "name" : "row4", "position" : { "scale" : 1, "translation-in-points" : [ 200.44268750000003, -51.15625 ] } }, { "fill" : { "solid" : "display-p3:0.02353,0.56863,1.00000,1.00000" }, "image-name" : "Untitled-2 3.svg", "name" : "row5", "position" : { "scale" : 1, "translation-in-points" : [ 137.8046875, 98.390625 ] } }, { "fill" : { "solid" : "display-p3:0.02353,0.56863,1.00000,1.00000" }, "glass" : true, "image-name" : "Untitled-3 2.svg", "name" : "row6", "position" : { "scale" : 1, "translation-in-points" : [ 75.16468750000001, 247.140625 ] } } ], "shadow" : { "kind" : "neutral", "opacity" : 0.5 }, "translucency" : { "enabled" : true, "value" : 0.5 } } ], "supported-platforms" : { "circles" : [ "watchOS" ], "squares" : "shared" } } ================================================ FILE: FSNotes/mul.lproj/Main.xcstrings ================================================ { "sourceLanguage" : "en", "strings" : { "0f7-Za-V0B.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Copyright © 2017-2024 Oleksandr Hlushchenko.\\nAll rights reserved.\"; ObjectID = \"0f7-Za-V0B\"; Note = \"#bc-ignore!\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nVšechna práva vyhrazena." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कॉपीराइट © 2017-2024 ऑलेक्ज़ेंडर ह्लुशेंको।\nसभी अधिकार सुरक्षित।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2023 Oleksandr Hlushchenko.\nВсе права защищены." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Telif hakkı © 2017-2024 Oleksandr Hlushchenko.\nHer hakkı saklıdır." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Copyright © 2017-2024 Oleksandr Hlushchenko.\nAll rights reserved." } } } }, "0o7-iB-dtg.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Activate:\"; ObjectID = \"0o7-iB-dtg\";", "extractionState" : "extracted_with_value", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", "value" : "Aktivieren:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Activate:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Activar:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Activer :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "सक्रिय करें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Attiva:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Активировать:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etkinleştir:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Активувати:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "激活:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "啟用:" } } } }, "0Qp-Is-dNs.placeholderString" : { "comment" : "Class = \"NSTextFieldCell\"; placeholderString = \"example.com\"; ObjectID = \"0Qp-Is-dNs\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "priklad.com" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "example.com" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "domainadi.com" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "example.com" } } } }, "1b7-l0-nxx.title" : { "comment" : "Class = \"NSMenu\"; title = \"Find\"; ObjectID = \"1b7-l0-nxx\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ابحث" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Suchen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Find" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Buscar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Rechercher" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חפש" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खोजे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Trova" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "検索" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "찾기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zoeken" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Найти" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ara" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Знайти" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "查找" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "尋找" } } } }, "1cO-zi-naR.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"BackLinks\"; ObjectID = \"1cO-zi-naR\";", "extractionState" : "extracted_with_value", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", "value" : "Backlinks" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "BackLinks" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Enlaces externos" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Liens retour" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "बैकलिंक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "BackLinks" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Обратные ссылки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "BackLinks" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зворотні посилання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "反向链接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "反向連結" } } } }, "1HB-X5-pmA.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"2 Spaces\"; ObjectID = \"1HB-X5-pmA\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "2 Spaces" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "2 mezery" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "2 Spaces" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "2 Spaces" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "2 Spaces" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "2 Spaces" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "2 Spaces" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "2 स्थान" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "2 Spaces" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "2 スペース" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "2 Spaces" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "2 Spaces" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "2 Spaces" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "2 Spaces" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "2 пробела" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "2 Boşluk" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "2 пробіли" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "缩进 2 个空格" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "2 個空格" } } } }, "1Kl-ap-nkv.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Locked\"; ObjectID = \"1Kl-ap-nkv\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Locked" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zamčeno" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Abgeschlossen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Locked" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Bloqueado" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Verrouillé" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Locked" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लॉक की गई" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Bloccato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ロック" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "잠김" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Gesloten" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Bloqueado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Bloqueado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заблокировано" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kilitli" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Замкнено" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "锁定" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "已鎖定" } } } }, "1qV-pJ-ZQT.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Title\"; ObjectID = \"1qV-pJ-ZQT\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عنوان" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nadpis" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Title" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Title" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Título" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Titre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "כותרת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タイトル" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "제목" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Titel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Título" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlık" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "標題" } } } }, "1sM-9Q-KeG.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"List\"; ObjectID = \"1sM-9Q-KeG\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "قائمة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Seznam" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Liste" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "List" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Lista" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Liste" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "רשימה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सूची" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Lista" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リスト" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "명부" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "List" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Lista" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Lista" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Список" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Liste" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Список" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "列表" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "清單" } } } }, "1Uk-jy-0qP.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Remove\"; ObjectID = \"1Uk-jy-0qP\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odebrat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Remove" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Remover" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "移除" } } } }, "1Xt-HY-uBw.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"FSNotes\"; ObjectID = \"1Xt-HY-uBw\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "FSNotes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } } } }, "1XY-bI-DX7.placeholderString" : { "comment" : "Class = \"NSTextFieldCell\"; placeholderString = \"TextEdit\"; ObjectID = \"1XY-bI-DX7\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "TextEdit" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "TextEdit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "TextEdit" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "TextEdit" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "TextEdit" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "TextEdit" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "TextEdit" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "TextEdit" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "TextEdit" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "TextEdit" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "텍스트 편집기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "TekstBewerken" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "TextEdit" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "TextEdit" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "TextEdit" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Metin Düzenle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Текстовий редактор" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "TextEdit" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "文字編輯" } } } }, "2CI-mu-aiB.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Dock Icon:\"; ObjectID = \"2CI-mu-aiB\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "رمز الإرساء:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Ikona:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Dock icon:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Dock Icon:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Icono del Dock:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Icône de Dock :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "צלמית Dock:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डॉक आइकन:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Icona Dock:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Dock アイコン:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Dock 아이콘:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Dock icoon:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ícone da dock:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ícone da dock:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Иконка в Dock:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dock Simgesi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Док іконка:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Dock栏图标:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Dock 圖示:" } } } }, "2Ci-Yj-aWK.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"New\"; ObjectID = \"2Ci-Yj-aWK\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "جديد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nový" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neu" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "New" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nuevo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nouveau" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חדש" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नया" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuovo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新規" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새로운 노트" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Nieuw" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Novo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Novo" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Новый" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нова нотатка" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "新建" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "新增" } } } }, "2Iq-vN-V2w.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"hour at\"; ObjectID = \"2Iq-vN-V2w\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ساعة في" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "hodin, vždy" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "stunde um" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "hour at" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "hora a las" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "heure à" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שעות ו-" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "घंटे पर" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "ore e" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "時間間隔 / 開始タイミング: " } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "시간 마다" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "uur bij" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "horas em" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "horas e" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "час в" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "saatte" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "годину" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "小时" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "時間於" } } } }, "2oI-Rn-ZJC.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Transformations\"; ObjectID = \"2oI-Rn-ZJC\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "التحولات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Transformace" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Transformationen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Transformations" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Transformaciones" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Transformations" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "המרות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "परिवर्तने" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Trasformazioni" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "変換" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "변형" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Transformaties" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Transformações" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Transformações" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Преобразования" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dönüşümler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Трансформація" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "转换" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "轉換" } } } }, "2un-du-hJz.title" : { "comment" : "Class = \"NSViewController\"; title = \"Git\"; ObjectID = \"2un-du-hJz\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Git" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गिट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Git" } } } }, "2Vh-rt-1kc.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"New Note in New Window \"; ObjectID = \"2Vh-rt-1kc\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "needs_review", "value" : "ملاحظة جديدة في نافذة جديدة" } }, "cs" : { "stringUnit" : { "state" : "needs_review", "value" : "Nové okno" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neu In neuem Fenster" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "New Note in New Window " } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nueva nota en ventana nueva" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nouveau dans une nouvelle fenêtre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הערה חדשה בחלון חדש" } }, "hi" : { "stringUnit" : { "state" : "needs_review", "value" : "नई विंडो" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuova nota in una nuova finestra" } }, "ja" : { "stringUnit" : { "state" : "needs_review", "value" : "新しいウィンドウで新しいメモ" } }, "ko" : { "stringUnit" : { "state" : "needs_review", "value" : "새 창에서 새 메모" } }, "nl-NL" : { "stringUnit" : { "state" : "needs_review", "value" : "Nieuwe notitie in nieuw venster" } }, "pt-BR" : { "stringUnit" : { "state" : "needs_review", "value" : "Nova janela" } }, "pt-PT" : { "stringUnit" : { "state" : "needs_review", "value" : "Nova nota em nova janela" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Новое заметка в новом окно" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni Pencere" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нова нотатка у новому вікні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "新窗口中的新笔记" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在新視窗新增筆記" } } } }, "3fK-Ap-A5p.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Move Item\"; ObjectID = \"3fK-Ap-A5p\";", "extractionState" : "extracted_with_value", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", "value" : "Element verschieben" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Move Item" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mover elemento" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déplacer l'élément" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "आइटम हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sposta elemento" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переместить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Öğeyi Taşı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перемістити елемент" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "移动项目" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "移動項目" } } } }, "3IN-sU-3Bg.title" : { "comment" : "Class = \"NSMenu\"; title = \"Spelling and Grammar\"; ObjectID = \"3IN-sU-3Bg\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "التهجئة والقواعد اللغوية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Pravopis a gramatika" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Rechtschreibung und Grammatik" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Spelling and Grammar" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ortografía y gramática" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Orthographe et grammaire" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "איות ודקדוק" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वर्तनी और व्याकरण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ortografia e Grammatica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "スペルと文法" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "맞춤법 및 문법" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Spelling en grammatica" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ortografia e Gramática" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ortografia e Gramática" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Орфография и грамматика" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yazım ve Dilbilgisi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Орфографія і граматика" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "拼写和语法" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "拼字與文法" } } } }, "3kn-9C-fsB.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Copy Title\"; ObjectID = \"3kn-9C-fsB\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نسخ العنوان" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kopírovat nadpis" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Title kopieren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Copy Title" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copiar título" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copier le titre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העתק כותרת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक कॉपी करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copia titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タイトルをコピー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "제목 복사" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kopieer titel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Copiar título" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Copiar título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Скопировать заголовок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlığı Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скопіювати заголовок" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "拷贝标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "複製標題" } } } }, "3RO-C7-NOO.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Change Master Password\"; ObjectID = \"3RO-C7-NOO\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تغيير كلمة السر الرئيسية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Změnit hlavní heslo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Master Password ändern" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Change Master Password" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cambiar contraseña maestra" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Changer le mot de passe" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה סיסמה ראשית" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मास्टर पासवर्ड बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cambia la Master Password" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "マスターパスワードを変更…" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "마스터 암호 변경" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verander Hoofdwachtwoord" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mudar senha mestra" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Palavra-passe Mestra" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Изменение мастер-пароля" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ana Parolayı Değiştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Змінити головний пароль" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更改管理员密码" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "變更主密碼" } } } }, "3rS-ZA-NoH.title" : { "comment" : "Class = \"NSMenu\"; title = \"Speech\"; ObjectID = \"3rS-ZA-NoH\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "كلام" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Řeč" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sprachausgabe" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Speech" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Habla" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Langage" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הקראה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "भाषण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Voce" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "スピーチ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "말하기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Spraak" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Fala" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Fala" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Диктовка" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Konuşma" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Промова" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "语音" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "語音" } } } }, "3rt-IC-kru.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"None Selected\"; ObjectID = \"3rt-IC-kru\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "None Selected " } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nic nevybráno" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Keine ausgewählt" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "None Selected" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ninguno Seleccionado" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "None Selected " } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "None Selected " } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोई भी नहीं चुना गया" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nessuno selezionato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "選択なし" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "선택되지 않음" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Geen geselecteerd" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nada selecionado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nenhum selecionado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Нет выбранных" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Hiçbiri Seçilmedi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не вибрано" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无选择" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "未選取" } } } }, "3tV-h0-YEb.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Open External\"; ObjectID = \"3tV-h0-YEb\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فتح" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Otevřít externí" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Öffnen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Open External" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Abrir" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פתח" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बाहरी खोलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apri" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "開く…" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "열기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Open" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Abrir External" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Abrir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открыть во внешнем" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Açık Harici" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відкрити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "打开" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在外部開啟" } } } }, "3zv-SD-lSX.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Create Folder\"; ObjectID = \"3zv-SD-lSX\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مجلد جديد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vytvořit složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neuer Ordner" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Create Folder" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nueva carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nouveau dossier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תיקיה חדשה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फोल्डर बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuova cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新規フォルダ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새로운 폴더" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Nieuwe map" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Criar pasta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nova pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать директорию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasör Oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нова директорія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "新建文件夹" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "新增資料夾" } } } }, "4cz-zj-dkH.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Sort By:\"; ObjectID = \"4cz-zj-dkH\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ترتيب حسب:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Řadit podle:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sortieren nach:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Sort By:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ordenar por:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Trier par :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מייו לפי:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इसके अनुसार क्रमबद्ध करें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ordina per:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "表示順序:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다음으로 정렬" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Sorteer op:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ordenar por:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ordenar por:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сортировать по:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Göre sırala:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сортувати за:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "排序方式:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "排序方式:" } } } }, "4EN-yA-p0u.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Find\"; ObjectID = \"4EN-yA-p0u\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "بحث" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Suchen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Find" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Buscar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Rechercher" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חפש" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खोजे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Trova" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "検索" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "찾기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zoeken" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Искать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ara" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Знайти" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "查找" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "尋找" } } } }, "4gs-Bc-GHG.placeholderString" : { "comment" : "Class = \"NSTextFieldCell\"; placeholderString = \"hlushchenko\"; ObjectID = \"4gs-Bc-GHG\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "hlushchenko" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "ह्लुशेंको" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "hlushchenko" } } } }, "4J7-dP-txa.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Enter Full Screen\"; ObjectID = \"4J7-dP-txa\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الدخول بشاشة كاملة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Spustit režim celé obrazovky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Vollbildmodus aktivieren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Enter Full Screen" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Usar pantalla completa" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Entrer en mode plein écran" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עבור למסך מלא" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पूर्ण स्क्रीन दर्ज करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Accedi a Schermo intero" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フルスクリーンにする" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "전체 화면 시작" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Schakel schermvullende weergave in" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Entrar no modo de tela cheia" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Entrar em modo de ecrã completo" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вход в полноэкранный режим" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tam Ekrana Yap" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Повноекранний режим" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "进入全屏幕" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "進入全螢幕" } } } }, "4sb-4s-VLi.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Quit FSNotes\"; ObjectID = \"4sb-4s-VLi\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الخروج من FSNotes" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Ukončit FSNotes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes beenden" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Quit FSNotes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Salir de FSNotes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Quitter FSNotes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "סיים FSNotes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes बंद करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Esci da FSNotes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FSNotesを終了" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes 종료" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Stop FSNotes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Sair do FSNotes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Sair do FSNotes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Выход из FSNotes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes'tan Çık" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Завершити роботу FSNotes" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "退出 FSNotes" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "結束 FSNotes" } } } }, "4Sj-5v-Clo.placeholderString" : { "comment" : "Class = \"NSTextFieldCell\"; placeholderString = \"git@github.com:username/repo.git\"; ObjectID = \"4Sj-5v-Clo\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "https://github.com/glushchenko/fsnotes.git" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:uzivatel/repo.git" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "https://github.com/glushchenko/fsnotes.git" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "git@github.com:username/repo.git" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "https://github.com/glushchenko/fsnotes.git" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "https://github.com/glushchenko/fsnotes.git" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "https://github.com/glushchenko/fsnotes.git" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:username/repo.git" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "https://github.com/glushchenko/fsnotes.git" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "https://github.com/glushchenko/fsnotes.git" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "https://github.com/glushchenko/fsnotes.git" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "https://github.com/glushchenko/fsnotes.git" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:username/repo.git" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "https://github.com/glushchenko/fsnotes.git" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:username/repo.git" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:kullanıcıadı/repo.git" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "https://github.com/glushchenko/fsnotes.git" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "https://github.com/glushchenko/fsnotes.git" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:username/repo.git" } } } }, "5gL-j7-Zq8.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Decrypt\"; ObjectID = \"5gL-j7-Zq8\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "needs_review", "value" : "ازالة التشفير" } }, "cs" : { "stringUnit" : { "state" : "needs_review", "value" : "Odebrat šifrování" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Entschlüsseln" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Decrypt" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar encriptación" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer le chiffrement" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסר הצפנה" } }, "hi" : { "stringUnit" : { "state" : "needs_review", "value" : "एन्क्रिप्शन हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Decrittografa" } }, "ja" : { "stringUnit" : { "state" : "needs_review", "value" : "ロックを解除" } }, "ko" : { "stringUnit" : { "state" : "needs_review", "value" : "암호화 해제" } }, "nl-NL" : { "stringUnit" : { "state" : "needs_review", "value" : "Verwijder versleuteling" } }, "pt-BR" : { "stringUnit" : { "state" : "needs_review", "value" : "Remover criptografia" } }, "pt-PT" : { "stringUnit" : { "state" : "needs_review", "value" : "Remover criptografia" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить шифрование" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Şifrelemeyi Kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити шифрування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "移除加密" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "解密" } } } }, "5kV-Vb-QxS.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"About FSNotes\"; ObjectID = \"5kV-Vb-QxS\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حول FSNotes" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "O aplikaci FSNotes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Über FSNotes" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "About FSNotes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Acerca de FSNotes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "A propos de FSNotes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אודות FSNotes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes के बारे में" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Informazioni su FSNotes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FSNotesについて" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes에 관하여" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Over FSNotes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Sobre o FSNotes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Acerca de FSNotes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Про FSNotes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes Hakkında" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Про FSNotes" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "关于 FSNotes" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "關於 FSNotes" } } } }, "5LV-Vi-n79.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Copy Title\"; ObjectID = \"5LV-Vi-n79\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نسخ العنوان" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kopírovat nadpis" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Title kopieren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Copy Title" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copiar título" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copier le titre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העתק כותרת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक कॉपी करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copia titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タイトルをコピー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "제목 복사" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kopieer titel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Copiar título" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Copiar título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Скопировать заголовок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlığı Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скопіювати заголовок" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "拷贝标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "複製標題" } } } }, "05n-RU-nOV.title" : { "comment" : "Class = \"NSMenu\"; title = \"Sort By\"; ObjectID = \"05n-RU-nOV\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ترتيب حسب" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Řadit podle" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sortieren nach" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Sort By" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ordenar por" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Trier par" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מיין לפי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इसके अनुसार क्रमबद्ध करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ordina per" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "表示順序" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다음으로 정렬" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Sorteer op" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ordenar por" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ordenar por" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сортировать по" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Göre sırala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сортувати за" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "排序方式" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "排序方式" } } } }, "5QF-Oa-p0T.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Edit\"; ObjectID = \"5QF-Oa-p0T\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تعديل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Upravit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bearbeiten" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Edit" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Editar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Éditer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עריכה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संपादन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Modifica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "編集" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "편집" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Edit" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Editar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Editar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Редактировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Düzenle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Редагування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "编辑" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "編輯" } } } }, "6AI-tL-TDI.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Hide images preview\"; ObjectID = \"6AI-tL-TDI\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اخفاء معاينة الصور" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Skrýt náhled obrázků" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bildvorschau ausblenden" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Hide images preview" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar la previsualización de imágenes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Masquer l'aperçu des images" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסתר תמונות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "छवियों का पूर्वावलोकन छिपाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nascondi anteprima immagini" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "画像プレビューを非表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이미지 미리보기 가리기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verberg voorvertoning afbeeldingen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar pré-visualização das imagens" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar pré-visualização de imagens" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Спрятать превью изображений" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Görüntü önizlemesini gizle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сховати прев'ю зображень" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "隐藏图像预览" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "隱藏圖片預覽" } } } }, "6dh-zS-Vam.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Redo\"; ObjectID = \"6dh-zS-Vam\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "استرجاع" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Opakovat akci" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Wiederholen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Redo" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Rehacer" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Refaire" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חזור על הפעולה האחרונה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फिर से करे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ripeti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "やり直し" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "실행 복귀" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Opnieuw doen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Refazer" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Refazer" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вперёд" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeniden yap" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Повторити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重做" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重做" } } } }, "6F0-Cb-DVX.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Code Theme:\"; ObjectID = \"6F0-Cb-DVX\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Code ثيم:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Motiv kódu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Code Design:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Code Theme:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Tema:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Theme de code :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ערכת נושא לקוד:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोड थीम:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Tema grafico del Codice:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "コードテーマ:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "코드 테마:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Code Thema:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tema do código:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tema de código:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Тема для кода:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kod Teması:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Тема коду:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "代码主题:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "程式碼主題:" } } } }, "7GB-y2-EMo.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Close\"; ObjectID = \"7GB-y2-EMo\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اغلاق" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zavřít" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Schließen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Close" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cerrar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Fermer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "סגור" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बंद करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Chiudi" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "閉じる" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "닫기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Sluit" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Fechar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Fechar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Закрыть" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kapat" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Закрити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "关闭" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "關閉" } } } }, "7iL-1X-EtS.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Clone\"; ObjectID = \"7iL-1X-EtS\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Klonovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Clone" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्लोन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Склонировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "克隆" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "複製專案 (Clone)" } } } }, "7me-g8-vOq.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Current Password:\"; ObjectID = \"7me-g8-vOq\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "كلمة المرور الحالية:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Současné heslo:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Aktuelle Passwort:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Current Password:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Contraseña actual:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mot de passe actuel" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "סיסמה נוכחית:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वर्तमान पासवर्ड:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Password corrente:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "現在のパスワード:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "기존 암호:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Huidig wachtwoord:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Senha atual" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Palavra-passe actual" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Текущий пароль:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Mevcut Şifre:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Поточний пароль:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "当前密码:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "目前密碼:" } } } }, "7MT-fy-lXN.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Open External\"; ObjectID = \"7MT-fy-lXN\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فتح خارجي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Otevřít externí" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In externem Editor öffnen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Open External" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Abrir externo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir en externe" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פתח חיצוני" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बाहरी खोलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apri esternamente" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "外部エディターで開く..." } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "외부에서 열기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Open extern" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Abrir External" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Abrir externamente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Внешний редактор" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Açık Harici" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відкрити зовні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "打开外部" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在外部開啟" } } } }, "7od-TM-cUB.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Set\"; ObjectID = \"7od-TM-cUB\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مجموعة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nastavit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Setzen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Set" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Escoger" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Appliquer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "בחר" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सेट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Imposta" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "設定" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "설정" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Instelling" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Definir" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Definir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Установить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ayarla" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Встановити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "设置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "設定" } } } }, "7xA-9l-dja.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Change\"; ObjectID = \"7xA-9l-dja\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "التغيير" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Změnit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ändern" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Change" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cambiar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Changer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cambia" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "변경" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verander" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Alterar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Modificar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Изменить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Değiştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Змінити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更改" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "變更" } } } }, "8AQ-IN-Edm.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Small\"; ObjectID = \"8AQ-IN-Edm\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "صغير" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Malé" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Klein" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Small" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Pequeño" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Petit" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "קטן" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "छोटा" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Piccolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "小さい" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "작게" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Klein" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pequeno" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pequeno" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Маленький" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Küçük" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Маленький" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "小" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "小" } } } }, "8cf-5K-KZh.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Ordered List\"; ObjectID = \"8cf-5K-KZh\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "قائمة مرتبة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Řazený seznam" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bestellliste" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Ordered List" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Lista ordenada" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Liste ordonnée" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "רשימה ממוספרת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्रमबद्ध सूची" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Lista Ordinata" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "番号付きリスト" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "주문 된 목록" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Ordered List" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Lista ordenada" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "List Ordenada" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Упорядоченный список" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sıralı Liste" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Упорядкований список" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "有序列表" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "編號清單" } } } }, "8dk-Cf-bSg.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Lock when user switched\"; ObjectID = \"8dk-Cf-bSg\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "قفل عند تحول المستخدم" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zamknout při přepnutí uživatele" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sperren, wenn der Benutzer wechselt" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Lock when user switched" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear al cambiar de usuario" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Verrouiller au changement d'utilisateur" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נעל בהחלפת משתמש" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "उपयोगकर्ता बदलने पर लॉक करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Blocca quando viene cambiato utente" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ユーザー切り替え時" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "사용자 변경시 잠금" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Vergrendel bij verandering van gebruiker" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear quando alterar usuário" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear quando alterar utilizador" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заблокировать при смене польз." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kullanıcı değiştiğinde kilitle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заблокувати при перемиканні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "用户切换时自动锁定" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "切換使用者時鎖定" } } } }, "8om-Y6-O8e.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Credentials:\"; ObjectID = \"8om-Y6-O8e\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "أوراق اعتماد:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přístupové údaje" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Berechtigungsnachweise:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Credentials:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cartas credenciales:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Identifiants:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אישורים:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "साख:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Credenziali:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "資格:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "신임장:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Inloggegevens:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Credenciais:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Credenciais:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Реквизиты для входа:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kimlik Bilgileri:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Облікові дані:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "证书:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "憑證:" } } } }, "9ic-FL-obx.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Substitutions\"; ObjectID = \"9ic-FL-obx\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "استبدالات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Záměny" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ersetzungen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Substitutions" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Sustituciones" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Substitutions" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "החלפות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रतिस्थापन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sostituzioni" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "自動置換" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "대체" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Vervangingen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Substituições" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Substituições" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Замены" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İkameler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заміни" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "替换" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "替代" } } } }, "9pM-sd-qhm.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Port:\"; ObjectID = \"9pM-sd-qhm\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Port:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Port:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Port:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Port:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Port:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Port:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Port:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पोर्ट:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Port:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Port:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Port:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Port:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Port:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Port:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Порт:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Port:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Порт:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "端口:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "連接埠:" } } } }, "9w3-fa-di3.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"2/23/11\"; ObjectID = \"9w3-fa-di3\"; Note = \"#bc-ignore!\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "2/23/11" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "2/23/11" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "2/23/11" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "2/23/11" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "2/23/11" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "2/23/11" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "" } } }, "shouldTranslate" : false }, "9yt-4B-nSM.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Smart Copy/Paste\"; ObjectID = \"9yt-4B-nSM\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نسخ/لصق الذكي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Inteligentní kopírování/vkládání" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Intelligente Kopieren / Einfügen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Smart Copy/Paste" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copiado/pegado Inteligente" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copier/Coller intelligent" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העתקה והדבקה חכמות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्मार्ट कॉपी/पेस्ट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copia/Incolla Smart" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "スマートコピー/ペースト" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "스마트 복사하기/붙여넣기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Slim Kopiëren/Plakken" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Copiar/colar inteligente" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Copiar/Colar Inteligente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Смарт-копирование/вставка" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Akıllı Kopyala/Yapıştır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розумне копіювання/вставка" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "智能拷贝/粘贴" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "智慧型複製/貼上" } } } }, "13t-eQ-kOr.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Translators:\"; ObjectID = \"13t-eQ-kOr\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مترجمين:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Překladatelé" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Translators:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Translators:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Traductores:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Traducteurs :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מתרגמים‫:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अनुवादक:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Traduttori:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "翻訳者:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "번역자:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Vertalers:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tradutores:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tradutores:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переводчики:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tercümanlar:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перекладачі:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "翻译:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "翻譯貢獻者:" } } } }, "44f-my-fi3.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Passphrase:\"; ObjectID = \"44f-my-fi3\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عبارة المرور:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přístupová fráze:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passprobe:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Passphrase:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Frase de contraseña:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ביטוי סיסמה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासफ्रेज:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Frase d'accesso:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "암호:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wachtwoordzin:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Frase secreta:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Senha:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Парольная фраза:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Parola:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Кодова фраза:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "密码:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "通關密語:" } } } }, "63m-us-3j9.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Code Font:\"; ObjectID = \"63m-us-3j9\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "خط الكود:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Písmo kódu:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Code Schrift:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Code Font:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Fuente:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Police de code :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גופן קוד:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोड फ़ॉन्ट:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Font Codice:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "コードフォント:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "코드 서체:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Code Lettertype:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Código fonte:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Letra de código:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Шрифт кода:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kod Yazı Tipi:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Шрифт коду:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "代码字体:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "程式碼字體:" } } } }, "78Y-hA-62v.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Correct Spelling Automatically\"; ObjectID = \"78Y-hA-62v\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تصحيح الإملاء تلقائيًا" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Automaticky opravovat pravopis" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Rechtschreibung automatisch korrigieren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Correct Spelling Automatically" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Corregir ortografía automáticamente" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Corriger automatiquement l'orthographe" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תקן איות באופן אוטומטי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वर्तनी स्वतः सही करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Correggi Ortografia Automaticamente" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "スペルを自動的に修正" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "맞춤법 자동 수정" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Corrigeer Spelling Automatisch" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Corrigir ortografia automaticamente" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Corrigir Ortografia Automáticamente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Автоматическое исправление орфографии" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Otomatik Olarak Doğru Yazımı Düzelt" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Автоматично коригувати правописання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "自动纠正拼写" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "自動修正拼字" } } } }, "a1w-ll-Djh.placeholderString" : { "comment" : "Class = \"NSTextFieldCell\"; placeholderString = \"empty log\"; ObjectID = \"a1w-ll-Djh\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "empty log" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खाली लॉग" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "log vazio" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Boş Log" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "清空記錄" } } } }, "A5V-Wm-if2.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Minimize\"; ObjectID = \"A5V-Wm-if2\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تصغير" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Minimalizovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Minimieren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Minimize" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Minimizar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Minimizer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מזער" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "छोटा करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Minimizza" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "しまう" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "최소화" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Minimaliseren" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Minimizar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Minimizar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Минимизировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Küçült" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Приховати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "最小化" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "縮到最小" } } } }, "a6n-hz-V8D.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Search shortcut:\"; ObjectID = \"a6n-hz-V8D\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اختصار البحث:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Verknüpfung suchen:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Search shortcut:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Función rápida de búsqueda:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Raccourci recherche:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "קיצור דרך לחפש:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खोज शॉर्टकट:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ricerca rapida:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "検索ショートカット:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "검색 단축키:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zoek snelkoppeling:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar atalho:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar atalho:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Поиск:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Arama kısayolu:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Швидкий пошук:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "搜索快捷方式:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "搜尋快速鍵:" } } } }, "a9t-hD-WYR.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Host:\"; ObjectID = \"a9t-hD-WYR\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Host:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hostitel:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Host:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Host:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Host:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Host:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Host:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "होस्ट:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Host:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Host:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Host:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Host:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Host:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Host:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Хост:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Host:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Хост:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "主机Host:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "主機:" } } } }, "aGv-BR-Oc8.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Upload and test initial data\"; ObjectID = \"aGv-BR-Oc8\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تحميل واختبار البيانات الأولية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nahrát a otestovat úvodní data" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Hochladen und Testen der ersten Daten" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Upload and test initial data" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cargar y probar datos iniciales" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Télécharger et tester les données initiales" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העלה ובדוק נתונים ראשוניים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रारंभिक डेटा अपलोड करें और उसका परीक्षण करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Carica e testa i dati iniziali" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "初期データのアップロードとテスト" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "초기 데이터 업로드 및 테스트" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Upload en test initiële gegevens" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Carregar e testar dados iniciais" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Carregar e testar dados iniciais" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Загрузить исходные данные и протестировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İlk verileri yükleyin ve test edin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Завантаження та перевірка вихідних даних" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "上传和测试初始数据" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "上傳並測試初始資料" } } } }, "aLg-MQ-amx.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Italic:\"; ObjectID = \"aLg-MQ-amx\";", "extractionState" : "extracted_with_value", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", "value" : "Kursiv:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Italic:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cursiva:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Italique :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "इतालिक:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Corsivo:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Курсив:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İtalik:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Курсив:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "斜体:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "斜體:" } } } }, "ASf-hd-2fT.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Repositories:\"; ObjectID = \"ASf-hd-2fT\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "المستودعات:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Repozitáře:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Repositories:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Repositories:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Repositorios:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dépôts :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מאגרים:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "रिपोजिटरी:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Repository:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リポジトリ:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "저장소:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Opslagplaatsen:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Repositórios:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Repositórios:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Репозитории:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Depolar:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Репозиторії:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "存储库:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "儲存庫:" } } } }, "Atd-Fs-Chq.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Origin:\"; ObjectID = \"Atd-Fs-Chq\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Git origin:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Origin:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Git origin:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Origin:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Git origin:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Git origin:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Git origin:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "उद्गम:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Git origin:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Git origin:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Git origin:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Git origin:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Origem:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Git origin:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Git ориджин:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Menşei:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Git origin:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Git Origin:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "來源 (Origin):" } } } }, "auW-wa-jbc.title" : { "comment" : "Class = \"NSMenu\"; title = \"History\"; ObjectID = \"auW-wa-jbc\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تاريخ" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Historie" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Versionsverlauf" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "History" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Historia" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Historique" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "היסטוריה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इतिहास" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cronologia" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "履歴" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "변경 이력" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Geschiedenis" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Histórico" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Histórico" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "История" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tarih" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Історія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "笔记历史版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "歷程記錄" } } } }, "aVa-yH-IpC.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Format: yyyyMMddHHmmss\"; ObjectID = \"aVa-yH-IpC\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "التنسيق: yyyyMMddHHmmss" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Formát: yyyyMMddHHmmss" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Format: yyyyMMddHHmmss" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Format: yyyyMMddHHmmss" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Formato: yyyyMMddHHmmss" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Format: yyyyMMddHHmmss" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פורמט: yyyyMMddHHmmss" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रारूप: yyyyMMddHHmmss" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Formato: yyyyMMddHHmmss" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォーマット: yyyyMMddHHmmss" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "체재: yyyyMMddHHmmss" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Format: yyyyMMddHHmmss" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Formato: yyyyMMddHHmmss" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Formato: yyyyMMddHHmmss" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Формат: yyyyMMddHHmmss" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Biçim: yyyyMMddHHmmss" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Формат: yyyyMMddHHmmss" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "格式: yyyyMMddHHmmss" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "格式:yyyyMMddHHmmss" } } } }, "aX0-yU-aew.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"iCloud Drive\"; ObjectID = \"aX0-yU-aew\"; Note = \"#bc-ignore!\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "iCloud Drive" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "iCloud ड्राइव" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Sürücüsü" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "iCloud 雲碟" } } } }, "axA-pN-Zf2.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Set\"; ObjectID = \"axA-pN-Zf2\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تعيين" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nastavit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Satz" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Set" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Establecer" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Régler" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Set" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सेट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Impostare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Set" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "세트" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Set" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Definir" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Definir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Выбрать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ayarla" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вибрати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "设置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "設定" } } } }, "AYu-sK-qS6.title" : { "comment" : "Class = \"NSMenu\"; title = \"Main Menu\"; ObjectID = \"AYu-sK-qS6\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "القائمة الرئيسية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hlavní nabídka" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Hauptmenü" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Main Menu" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Menú principal" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Menu principal" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Main Menu" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मुख्य मेन्यू" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Menù principale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "メインメニュー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "주 메뉴" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hoofdmenu" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Menu principal" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Menu Principal" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Основное Меню" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ana Menü" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Головне меню" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "主菜单" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "主選單" } } } }, "B7t-9v-bSk.placeholderString" : { "comment" : "Class = \"NSSearchFieldCell\"; placeholderString = \"Search or create\"; ObjectID = \"B7t-9v-bSk\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "بحث او انشاء" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat / vytvořit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Suchen oder erstellen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Search or create" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Buscar o crear" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Rechercher ou créer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חיפוש ויצירה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खोजें या बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cerca o crea" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "検索/新規作成" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "검색 및 추가" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zoek of maak" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar ou criar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Procurar ou criar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Найти или создать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ara veya oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Знайти або створити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "搜索或创建(按下回车即可创建)" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "搜尋或新增" } } } }, "bCL-eg-DgP.title" : { "comment" : "Class = \"NSMenuItem\"; title = \".md\"; ObjectID = \"bCL-eg-DgP\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "en" : { "stringUnit" : { "state" : "new", "value" : ".md" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : ".md" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : ".md" } } } }, "bdD-JA-K8P.placeholderString" : { "comment" : "Class = \"NSTextFieldCell\"; placeholderString = \"/var/www/example.com\"; ObjectID = \"bdD-JA-K8P\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/priklad.com" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "/var/www/example.com" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "/var/www/example.com" } } } }, "BHF-CB-P9C.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Change\"; ObjectID = \"BHF-CB-P9C\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تغيير" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Změnit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ändern" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Change" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cambiar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Changer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cambia" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Change" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verander" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Alterar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Modificar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Изменить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Değiştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Змінити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更改" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "變更" } } } }, "bJC-he-nDV.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Note Font:\"; ObjectID = \"bJC-he-nDV\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "خط الملاحظة:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Písmo poznámky:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notizschrift:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Note Font:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Fuente:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Police :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גופן פתקים:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट फ़ॉन्ट:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Font Note:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォント:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "노트 서체:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Notitie Lettertype:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Fonte da nota:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tipo de letra de notas:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Шрифт:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Not Yazı Tipi:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Шрифт:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "笔记字体:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "筆記字體:" } } } }, "bJv-E9-bwW.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Show icon in dock\"; ObjectID = \"bJv-E9-bwW\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إظهار الأيقونة في شريط الايقونات " } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit ikonu v Docku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Icon in dock anzeigen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Show icon in dock" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar icono en el Dock" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher l'icône dans le Dock" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג צלמית ב-Dock" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डॉक में आइकन दिखाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra icona nel dock" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Dockにアイコンを表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Dock에서 아이콘 보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon icoon in dock" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar ícone na dock" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostar icone na dock" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показывать иконку в доке" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dock'ta simgeyi göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відображати іконку в док панелі" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在Dock栏中显示图标" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在 Dock 中顯示圖示" } } } }, "BoD-Jy-zeE.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Clear Completed Todos\"; ObjectID = \"BoD-Jy-zeE\";", "extractionState" : "extracted_with_value", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", "value" : "Abgeschlossene Aufgaben löschen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Clear Completed Todos" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Borrar tareas completadas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Effacer les tâches terminées" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "पूरे हुए कार्य हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cancella attività completate" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Очистить выполненные задачи" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tamamlanan Görevleri Temizle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Очистити виконані завдання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "清除已完成事项" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "清除已完成的待辦事項" } } } }, "BOF-NM-1cW.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Preferences…\"; ObjectID = \"BOF-NM-1cW\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "التفضيلات ..." } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Předvolby…" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einstellungen…" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Preferences…" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Preferencias…" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Préférences…" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העדפות..." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्राथमिकताएं..." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Preferenze…" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "環境設定…" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "환경설정..." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voorkeuren..." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Preferências..." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Preferências..." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Настройки…" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tercihler…" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Налаштування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "偏好设置…" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "偏好設定…" } } } }, "Bri-cl-z86.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"minutes\"; ObjectID = \"Bri-cl-z86\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الدقائق" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "minut po celé" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "minutes" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "minutes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "minuto" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "minutes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "דקות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मिनट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "minuti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "分" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "분에 실행" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "minuten" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "minutos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "minutos" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "минуту часа" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "dakikalar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "хвилин" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "分钟" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "分鐘" } } } }, "BtV-Z4-7Vq.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Move Down in the Notes List\"; ObjectID = \"BtV-Z4-7Vq\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تحرك لأسفل في قائمة الملاحظات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Posunout dolů v seznamu poznámek" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In der Notizliste nach unten gehen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Move Down in the Notes List" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mover abajo en la lista de notas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Note suivante" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עבור למטה ברשימת הפתקים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट्स सूची में नीचे जाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Muovi in basso nella Lista Note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "下に移動" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "메모 목록에서 아래로 이동" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Move Down In The Notes List" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mover para baixo na lista de notas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Descer na lista de Notas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Перейти вниз в списке заметок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notlar Listesinde Aşağıya Git" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейти вниз у списку нотаток" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在笔记列表中下移" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在筆記清單中下移" } } } }, "buJ-ug-pKt.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Use Selection for Find\"; ObjectID = \"buJ-ug-pKt\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "استخدم التحديد للبحث" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat výběr" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Auswahl suchen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Use Selection for Find" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Usar selección para buscar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Utiliser la sélection pour Rechercher" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "השתמש במלל הנבחר לחיפוש" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खोजने के लिए चयन का उपयोग करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Usa Selezione per Trovare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "選択部分を検索に使用" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "선택 부분으로 찾기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Gebruik selectie voor Zoeken" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Usar seleção para procurar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Utilizar selecção para Procura" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Использовать выбранное для поиска" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seçimi Bulmak İçin Kullan" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Використати вибране для пошуку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "使用选择查找" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "使用選取範圍進行尋找" } } } }, "bUL-WS-rIS.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Publish notes to:\"; ObjectID = \"bUL-WS-rIS\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "انشر الملاحظات على:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zveřejnit poznámky na:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Veröffentlichen Sie Notizen an:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Publish notes to:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Publicar notas sobre:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Publier des notes sur:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פרסם הערות על:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट प्रकाशित करें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Pubblica note su:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "以下に関するメモを公開します。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다음에 대한 메모 게시:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Publiceer notities over:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Publicar notas para:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Publicar notas para:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Публиковать заметки на:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notları şuraya yayınla:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Публікувати нотатки на:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "发布笔记到:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "發布筆記至:" } } } }, "BX0-nb-tVk.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Untagged\"; ObjectID = \"BX0-nb-tVk\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "بدون علامات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Neoznačené" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ungetaggt" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Untagged" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Sin etiquetar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sans libellé" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ללא תגים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बिना टैग" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Note senza Tag" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タグ無し" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "태그가 없는" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Niet gelabeld" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Sem tag" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Sem tag" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Без тегов" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etiketsiz" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Без тегів" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无标签" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "未加標籤" } } } }, "bXK-wP-sZc.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Note List Spacing:\"; ObjectID = \"bXK-wP-sZc\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "التباعد في قائمة الملاحظات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Rozestup v seznamu poznámek:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Listenhöhe:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Note List Spacing:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Espaciado en la lista:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Espacement :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מרווח רשימות פתקים:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट सूची रिक्ति:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Spaziatura elenco note:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ノートリストのマージン:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "노트 목록 간격:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Notitie lijst spatiëring:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Espaçamento da lista de notas:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Espaçamento da lista:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Высота в списке:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Not Listesi Aralığı:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відстань між нотатками:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "笔记列表间距:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "筆記清單間距:" } } } }, "c8a-y6-VQd.title" : { "comment" : "Class = \"NSMenu\"; title = \"Transformations\"; ObjectID = \"c8a-y6-VQd\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "التحولات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Transformace" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Transformations" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Transformations" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Transformaciones" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Transformations" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "המרות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "परिवर्तने" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Trasformazioni" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "変換" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "변형" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Transformaties" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Transformações" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Transformações" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Трансформация" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dönüşümler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Трансформація" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "转换" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "轉換" } } } }, "cfe-sv-2gm.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Official Site\"; ObjectID = \"cfe-sv-2gm\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الموقع الرسمي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Oficiální stránka" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Offizielle Seite" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Official Site" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Sitio web de FSNotes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Site officiel" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אתר רשמי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "आधिकारिक साइट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sito ufficiale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "公式サイト" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "공식 사이트" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Officiële site" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Site oficial" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Site oficial" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Официальный сайт" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Resmi Site" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Офіційна сторінка" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "官方网址" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "官方網站" } } } }, "cmH-fQ-bVK.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Format: UUID\"; ObjectID = \"cmH-fQ-bVK\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "UUID" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Formát: UUID" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "UUID" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Format: UUID" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "UUID" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "UUID" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פורמט: UUID" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रारूप: UUID" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "UUID" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォーマット: UUID" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "UUID" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "UUID" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Formato: UUID" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "UUID" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "UUID" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Biçim: UUID" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "UUID" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "UUID" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "格式:UUID" } } } }, "cq7-SM-dez.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Header 6\"; ObjectID = \"cq7-SM-dez\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عنوان ٦" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nadpis 6" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Header 6" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Header 6" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Encabezado 6" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Titre 6" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "כותרת 6" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हेडर 6" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Intestazione 6" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ヘッダー 6" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Header 6" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kop 6" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Cabeçalho 6" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Cabeçalho 6" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок 6" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlık 6" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок 6" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "6级标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "標題 6" } } } }, "cQ8-sF-a4g.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Remove\"; ObjectID = \"cQ8-sF-a4g\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odebrat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Remove" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Remover" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Remove" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "移除" } } } }, "ctD-Qn-kDS.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Format:\"; ObjectID = \"ctD-Qn-kDS\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تنسيق:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Formát:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Format:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Format:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Formato:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Format :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פורמט:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रारूप:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Formato:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォーマット:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "형식:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Formaat:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Formato:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Formato:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Расширение:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Biçim:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розширення:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "编辑器格式:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "格式:" } } } }, "cUI-cw-eo5.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Big\"; ObjectID = \"cUI-cw-eo5\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "كبير" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Velké" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Groß" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Big" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Grande" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Grand" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גדול" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बड़ा" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Grande" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "大きい" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "크게" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Groot" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Grande" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Grande" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Большой" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Büyük" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Великий" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "较大" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "大" } } } }, "CvG-Kp-kdK.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Move\"; ObjectID = \"CvG-Kp-kdK\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نقل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přesunout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Verschieben" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Move" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mover" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déplacer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העבר" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्थानांतरण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sposta" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "移動" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이동" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verplaats" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mover" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mover" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переместить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Taşı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перемістити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "移动" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "移動" } } } }, "cwL-P1-jid.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Smart Links\"; ObjectID = \"cwL-P1-jid\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "روابط ذكية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Inteligentní odkazy" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Intelligente Links" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Smart Links" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Enlaces inteligentes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Liens intelligents" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "קישורים חכמים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्मार्ट लिंक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Link Smart" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "スマートリンク" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "스마트 링크" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Slimme Links" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Links inteligentes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ligações Inteligentes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Смарт-ссылки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Akıllı Bağlantılar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розумні посилання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "智能链接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "智慧型連結" } } } }, "cYB-6U-8ac.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Header 2\"; ObjectID = \"cYB-6U-8ac\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عنوان ٢" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nadpis ě" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Header 2" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Header 2" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Encabezado 2" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Titre 2" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "כותרת 2" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हेडर 2" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Intestazione 2" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ヘッダー 2" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Header 2" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kop 2" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Cabeçalho 2" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Cabeçalho 2" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок 2" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlık" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок 2" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "2级标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "標題 2" } } } }, "d9M-CD-aMd.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Make Lower Case\"; ObjectID = \"d9M-CD-aMd\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اجعله حروف صغيرة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Převést na malá písmena" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Kleinschreiben" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Make Lower Case" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Todo en minúsculas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Passer en minuscules" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הפוך לאותיות קטנות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लोअर केस बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rendi tutto Minuscolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "小文字にする" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "소문자로 만들기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Maak kleine letters" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tornar minúsculas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Transformar em minúsculas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Перевести в нижний регистр" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Küçük Harf Yap" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нижній регістр" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "转换为小写" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "變更為小寫" } } } }, "dCc-l5-XB2.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Modification Date\"; ObjectID = \"dCc-l5-XB2\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تاريخ التعديل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Datum změny" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Änderungsdatum" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Modification Date" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Fecha de modificación" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Date de modification" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תאריך שינוי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सुधार की तारीख" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Data di modifica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "変更日" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "수정일" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wijzigingsdatum" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Data de modificação" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Data de modificação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Дата модификации" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Değişiklik Tarihi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Датою модифікації" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "修改日期" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "修改日期" } } } }, "DCh-5P-PZe.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Hide/Show Sidebar\"; ObjectID = \"DCh-5P-PZe\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إخفاء / إظهار الشريط الجانبي\n" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Skrýt/zobrazit boční panel" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Seitenleiste ein/ausblenden" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Hide/Show Sidebar" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar/ocultar barra lateral" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Masquer/Afficher la barre latérale" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסתר/הצג סרגל צד" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "साइडबार छिपाएँ/दिखाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nascondi/Mostra sidebar" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サイドバーを表示/非表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "사이드바 숨기기/보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verbergen/tonen zijbalk" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar/mostrar menu lateral" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar/mostrar barra lateral" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Спрятать/показать сайдбар" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kenar Çubuğunu Gizle/Göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Приховати/показати сайдбар" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "隐藏/显示侧边栏" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "隱藏/顯示側邊欄" } } } }, "DcN-1g-hEi.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Medium\"; ObjectID = \"DcN-1g-hEi\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "متوسط" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Střední" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Mittel" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Medium" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mediano" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Moyen" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "בינוני" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मध्यम" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Medio" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "普通" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "보통" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Medium" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Médio" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Médio" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Средний" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Orta" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Середній" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "中等的" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "中" } } } }, "DeU-hy-pvi.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Shift Left\"; ObjectID = \"DeU-hy-pvi\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تحول اليسار" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Posunout doleva" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Text nach links bewegen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Shift Left" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Tabular a la izquierda" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Décaler vers la gauche" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הזחה שמאלה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बाईं ओर शिफ्ट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sposta a sinistra" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "左にシフト" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "들여쓰기 삭제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verschuif Links" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mover para direita" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Deslocar à Esquerda" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сдвиг влево" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sola kaydır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зміщення вліво" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "左移" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "向左位移" } } } }, "dgD-xe-DVG.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"v2.9.0\"; ObjectID = \"dgD-xe-DVG\"; Note = \"#bc-ignore!\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "v2.9.0" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "v2.9.0" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "v2.9.0" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "v2.9.0" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "v2.9.0" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "v2.9.0" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "v2.9.0" } } } }, "doI-Mu-yZG.placeholderString" : { "comment" : "Class = \"NSTextFieldCell\"; placeholderString = \"no key\"; ObjectID = \"doI-Mu-yZG\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "bez klíče" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "no key" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोई चाबी नहीं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "ключ не выбран" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無金鑰" } } } }, "dop-ho-N26.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Separate .git in project dir (except iCloud Drive)\"; ObjectID = \"dop-ho-N26\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Separate .git in project dir (except iCloud Drive)" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Oddělený .git soubor ve složce projektu (kromě iCloud Drive)" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Separate .git in project dir (except iCloud Drive)" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Separate .git in project dir (except iCloud Drive)" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Separate .git in project dir (except iCloud Drive)" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Separate .git in project dir (except iCloud Drive)" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Separate .git in project dir (except iCloud Drive)" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रोजेक्ट निर्देशिका से .git अलग करे (iCloud ड्राइव को छोड़कर)" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Separate .git in project dir (except iCloud Drive)" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Separate .git in project dir (except iCloud Drive)" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Separate .git in project dir (except iCloud Drive)" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Separate .git in project dir (except iCloud Drive)" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Separate .git in project dir (except iCloud Drive)" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Separate .git in project dir (except iCloud Drive)" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : ".git в каталоге проекта (кроме iCloud Drive)" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Proje dizininde .git'i ayırın (iCloud Drive hariç)" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Separate .git in project dir (except iCloud Drive)" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在项目目录中分隔.git (iCloud 除外)" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在專案目錄中分離 .git(iCloud 雲碟除外)" } } } }, "Dp7-4n-ilt.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Custom Server\"; ObjectID = \"Dp7-4n-ilt\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "خادم مخصص" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vlastní server" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Benutzerdefinierter Server" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Custom Server" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Servidor personalizado" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Serveur personnalisé" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שרת מותאם אישית" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कस्टम सर्वर" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Server personalizzato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "カスタムサーバー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "커스텀 서버" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Aangepaste server" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Servidor personalizado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Servidor personalizado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пользовательский сервер" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Özel Sunucu" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Користувацький сервер" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "自定义服务器" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "自訂伺服器" } } } }, "dqy-22-ETG.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Decrypt Folder\"; ObjectID = \"dqy-22-ETG\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "needs_review", "value" : "تشفير المجلد" } }, "cs" : { "stringUnit" : { "state" : "needs_review", "value" : "Zašifrovat složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner entschlüsseln" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Decrypt Folder" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cifrar carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Crypter le dossier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצפנת תיקיה" } }, "hi" : { "stringUnit" : { "state" : "needs_review", "value" : "फ़ोल्डर एन्क्रिप्ट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Decrittografa cartella" } }, "ja" : { "stringUnit" : { "state" : "needs_review", "value" : "フォルダの暗号化" } }, "ko" : { "stringUnit" : { "state" : "needs_review", "value" : "폴더 암호화" } }, "nl-NL" : { "stringUnit" : { "state" : "needs_review", "value" : "Map versleutelen" } }, "pt-BR" : { "stringUnit" : { "state" : "needs_review", "value" : "Criptografar pasta" } }, "pt-PT" : { "stringUnit" : { "state" : "needs_review", "value" : "Criptografar pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Зашифровать директорию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasörü Şifrele" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розшифрувати директорію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "加密文件夹" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "解密資料夾" } } } }, "dRJ-4n-Yzg.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Undo\"; ObjectID = \"dRJ-4n-Yzg\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تراجع" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odvolat akci" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Rückgängig machen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Undo" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Deshacer" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Annuler" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ביטול" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पूर्ववत" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Indietro" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "取り消す" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "실행 취소" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Ongedaan maken" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Desfazer" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Desfazer" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Отменить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Geri al" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відмінити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "撤销" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "復原" } } } }, "Dv1-io-Yv7.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Spelling and Grammar\"; ObjectID = \"Dv1-io-Yv7\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "التهجئة والقواعد اللغوية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Pravopis a gramatika" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Rechtschreibung und Grammatik" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Spelling and Grammar" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ortografía y gramática" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Orthographe et grammaire" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "איות ודקדוק" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वर्तनी और व्याकरण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ortografia e Grammatica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "スペルと文法" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "맞춤법 및 문법" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Spelling en Grammatica" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ortografia e Gramática" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ortografia e Gramática" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Орфография и грамматика" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yazım ve Dilbilgisi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Орфографія і граматика" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "拼写和语法" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "拼字與文法" } } } }, "DWe-bx-jfM.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Header 5\"; ObjectID = \"DWe-bx-jfM\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عنوان ٥" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nadpis 5" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Header 5" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Header 5" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Encabezado 5" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Titre 5" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "כותרת 5" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हेडर 5" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Intestazione 5" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ヘッダー 5" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Header 5" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kop 5" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Cabeçalho 5" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Cabeçalho 5" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок 5" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlık 5" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок 5" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "5级标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "標題 5" } } } }, "DXE-kd-1X3.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Password:\"; ObjectID = \"DXE-kd-1X3\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "كلمة المرور:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Heslo:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Kennwort:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Password:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Clave:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mot de passe:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "סיסמה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासवर्ड:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Parola d'ordine:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスワード:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "비밀번호:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wachtwoord:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Senha:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Senha:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пароль:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Şifre :" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Пароль:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "密码" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "密碼:" } } } }, "dZD-Db-KHs.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Language:\"; ObjectID = \"dZD-Db-KHs\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اللغة (تحتاج إلى إعادة تشغيل التطبيق):" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Jazyk:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sprache (Neustart erforderlich):" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Language:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Idioma (requiere reiniciar):" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Langue (redémarrage de l'app) :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שפה (דורש אתחול):" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "भाषा:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Lingua:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "言語 (再起動後に反映):" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "언어 (재시작 필요)" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Taal (start app opnieuw op):" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Idioma:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Idioma (reinicia a aplicação)" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Язык (требуется перезапуск):" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dil :" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Мова (потрібен перезапуск):" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "语言 (需要重启FSNotes):" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "語言:" } } } }, "e0G-y0-N0C.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Todo\"; ObjectID = \"e0G-y0-N0C\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "قائمة المهام" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Úkoly" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Todo" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Todo" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Pendientes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Tâches" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מטלות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टुडू" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Da Fare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タスク" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "할 일" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Te Doen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "A fazer" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tarefa" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Задачи" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yapılacaklar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Завдання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "待办事项" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "待辦事項" } } } }, "E1U-fG-XBw.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"SSH configuration:\"; ObjectID = \"E1U-fG-XBw\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "SSH ترتيب:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "SSH konfigurace:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "SSH-Konfiguration:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "SSH configuration:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "SSH configuración:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Paramétrage SSH:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "SSH תְצוּרָה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "SSH कॉन्फ़िगरेशन:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Configurazione SSH:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "SSH 構成:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "SSH 구성:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "SSH configuratie:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "SSH configuração:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "SSH configuração:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Конфигурация SSH:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "SSH yapılandırması:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "SSH конфігурація:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "SSH 配置:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "SSH 設定:" } } } }, "E6I-n5-dsi.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Duplicate\"; ObjectID = \"E6I-n5-dsi\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عمل نسخة مكررة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Duplikovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Duplizieren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Duplicate" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Duplicar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dupliquer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שכפל" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नकल" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Duplica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "複製" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "복제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Dupliceren" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Duplicar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Duplicar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать копию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Дублювати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "副本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "製作複本" } } } }, "e7y-7Z-36y.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"_\"; ObjectID = \"e7y-7Z-36y\";", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "_" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "_" } } }, "shouldTranslate" : false }, "e15-ps-th1.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Hide date\"; ObjectID = \"e15-ps-th1\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اخفاء التاريخ" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Skrýt datum" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Datum ausblenden" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Hide date" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar la fecha" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Masquer la date" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסתר תאריך" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "तारीख छुपाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nascondi data" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "日付を非表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "날짜 숨기기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verberg datum" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar data" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar data" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Спрятать дату" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tarihi gizle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сховати дату" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "隐藏日期" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "隱藏日期" } } } }, "EC3-YF-sGZ.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Preview\"; ObjectID = \"EC3-YF-sGZ\"; Note = \"#bc-ignore!\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Náhled" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Preview" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पूर्वावलोकन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Önizleme" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Попередній перегляд" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "" } } }, "shouldTranslate" : false }, "eCt-xc-KgN.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Share\"; ObjectID = \"eCt-xc-KgN\";", "extractionState" : "extracted_with_value", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", "value" : "Teilen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Share" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Compartir" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Partager" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "साझा करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Condividi" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Поделиться" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Paylaş" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Поділитися" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "分享" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "分享" } } } }, "egX-yM-Suq.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Path:\"; ObjectID = \"egX-yM-Suq\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Path:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Cesta:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Pfad:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Path:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Path:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Path:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Path:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पथ:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sentiero:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Path:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Path:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Path:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Caminho:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Path:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Путь:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yol:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Шлях:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "路径:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "路徑:" } } } }, "Ehk-CU-fbX.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Set\"; ObjectID = \"Ehk-CU-fbX\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مجموعة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nastavit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Setzen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Set" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Escoger" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Appliquer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "בחר" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सेट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Imposta" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "設定" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "설정" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Instelling" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Definir:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Definir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Установить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ayarla" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Встановити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "设置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "設定" } } } }, "EK1-kN-kK3.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Strikethrough\"; ObjectID = \"EK1-kN-kK3\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نص يتوسطه خط" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přeškrtnuté" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Durchgestrichen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Strikethrough" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Tachado" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Barrer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "קו חוצה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्ट्राइकथ्रू" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Barrato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "取り消し線" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "취소선" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Doorhalen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Riscar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Suprimir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Перечёркнутый" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Üstü çizili" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перекреслений" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除线" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "刪除線" } } } }, "eKQ-lM-8Z4.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Font preview\"; ObjectID = \"eKQ-lM-8Z4\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "معاينة الخط" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Náhled písma" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Font Vorschau" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Font preview" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Font preview" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aperçu de la police" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תצוגת גופן" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ॉन्ट पूर्वावलोकन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Anteprima Font" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォントプレビュー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "서체 미리보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Lettertype voorvertoning" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pré-visualização da fonte" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pré-visualizar tipo de letra" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Предпросмотр шрифта" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yazı tipi önizlemesi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Шрифт попереднього перегляда" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "字体预览" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "字體預覽" } } } }, "ELw-Y4-DV0.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Version\"; ObjectID = \"ELw-Y4-DV0\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الاصدار" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Verze" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Version" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Version" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Versión" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Version" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גרסה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संस्करण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Versione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "バージョン" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "버전" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Versie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Versão" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Versão" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Версия" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Versiyon" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Версія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "版本" } } } }, "eTL-dh-GvM.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Creation Date\"; ObjectID = \"eTL-dh-GvM\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تاريخ الانشاء" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Datum vytvoření" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erstellungsdatum" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Creation Date" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Fecha de creación" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Date de création" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תאריך יצירה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "निर्माण तारीख" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Data di creazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "作成日" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "생성일" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Aanmaakdatum" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Data de criação" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Data de criação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Дата создания" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Oluşturulma Tarihi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Датою створення" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "创建日期" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "建立日期" } } } }, "EYG-WM-BFN.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Ascending\"; ObjectID = \"EYG-WM-BFN\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تصاعدي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vzestupně" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Aufsteigend" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Ascending" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ascendente" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ascendant" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עולה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "आरोही" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Crescente" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "昇順" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "오름차순" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Oplopend" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ascendente" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ascendente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Восходящее" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yükselen" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Висхідний" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "升序" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "遞增" } } } }, "F2S-fz-NVQ.title" : { "comment" : "Class = \"NSMenu\"; title = \"Help\"; ObjectID = \"F2S-fz-NVQ\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مساعدة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nápověda" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Hilfe" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Help" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ayuda" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aide" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עזרה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सहायता" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Aiuto" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ヘルプ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "도움말" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Help" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ajuda" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ajuda" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Помощь" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yardım" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Допомога" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "帮助" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "輔助說明" } } } }, "F6G-ua-MNX.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Hide preview\"; ObjectID = \"F6G-ua-MNX\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اخفاء المعاينة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Skrýt náhled textu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Vorschau ausblenden" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Hide preview" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar la previsualización de notas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Masquer l'apercu" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסתר תצוגה מקדימה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पूर्वावलोकन छुपाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nascondi anteprima note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リストプレビューを非表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "미리보기 숨기기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verberg voorvertoning" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar pré-visualização" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar pré-visualização" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Отключить предпросмотр" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Önizlemeyi gizle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сховати прев'ю" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "隐藏预览" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "隱藏預覽" } } } }, "f33-1h-Hvh.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Show nested folders content\"; ObjectID = \"f33-1h-Hvh\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إظهار محتوى المجلدات المتداخلة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit obsah podsložek" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Show nested folders content" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Show nested folders content" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar el contenido de las carpetas anidadas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher le contenu sous-jacent" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג תכני תיקיה מקוננים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नेस्टेड फ़ोल्डर्स सामग्री दिखाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra il contenuto delle cartelle" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "深い階層にあるフォルダの内容も表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "중첩 폴더 콘텐츠 표시" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Show nested folders content" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar conteúdo das pastas aninhadas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostarr conteúdo das pastas contíguas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показывать содержимое подпапок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İç içe klasörlerin içeriğini göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показувати вміст підпапок" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "显示嵌套文件夹内容" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示巢狀資料夾內容" } } } }, "FeM-D8-WVr.title" : { "comment" : "Class = \"NSMenu\"; title = \"Substitutions\"; ObjectID = \"FeM-D8-WVr\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تبديلات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Záměny" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ersetzungen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Substitutions" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Sustituciones" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Substitutions" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "החלפות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रतिस्थापन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sostituzioni" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "自動置換" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "대체" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Vervangingen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Substituições" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Substituições" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Подстановка" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İkameler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заміни" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "替换" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "替代" } } } }, "fHG-zk-g0k.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Delete\"; ObjectID = \"fHG-zk-g0k\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حذف" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Smazat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Löschen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Delete" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מחק" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मिटाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Elimina" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "삭제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verwijder" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Deletar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sil" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "刪除" } } } }, "FKE-Sm-Kum.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"FSNotes Help\"; ObjectID = \"FKE-Sm-Kum\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes مساعدة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nápověda pro FSNotes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes Hilfe" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "FSNotes Help" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ayuda de FSNotes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aide de FSNotes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עזרה בנושא FSNotes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes सहायता" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Guida di FSNotes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FSNotesヘルプ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes 도움말" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes-hulp" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ajuda do FSNotes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ajuda do FSNotes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Помощь FSNotes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes Yardım" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Допомога FSNotes " } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes帮助" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes 輔助說明" } } } }, "FKf-Ph-LiA.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Icons design:\"; ObjectID = \"FKf-Ph-LiA\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تصميم الأيقونات:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Design ikon:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Icons design:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Icons design:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Icons design:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Design des icônes :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עיצוב צלמיות:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "चिह्न डिजाइन:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Design icone:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アイコンデザイン:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "아이콘 디자인:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Pictogrammen ontwerp:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Design dos ícones:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Design dos ícones:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Дизайн иконки:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Simge tasarımı:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Дизайн іконок:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "图标设计:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "圖示設計:" } } } }, "fkH-Wf-n87.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Decrypt\"; ObjectID = \"fkH-Wf-n87\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "needs_review", "value" : "ازالة التشفير" } }, "cs" : { "stringUnit" : { "state" : "needs_review", "value" : "Odebrat šifrování" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Entschlüsseln" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Decrypt" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Quitar encriptación" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer le chiffrement" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסר הצפנה" } }, "hi" : { "stringUnit" : { "state" : "needs_review", "value" : "एन्क्रिप्शन हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Decrittografa" } }, "ja" : { "stringUnit" : { "state" : "needs_review", "value" : "ロックを削除" } }, "ko" : { "stringUnit" : { "state" : "needs_review", "value" : "암호화 해제" } }, "nl-NL" : { "stringUnit" : { "state" : "needs_review", "value" : "Verwijder versleuteling" } }, "pt-BR" : { "stringUnit" : { "state" : "needs_review", "value" : "Remover criptografia" } }, "pt-PT" : { "stringUnit" : { "state" : "needs_review", "value" : "Remover criptografia" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Снять шифрование" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Şifrelemeyi Kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розшифрувати нотатку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "移除加密" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "解密" } } } }, "fmS-eE-nne.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Default Storage:\"; ObjectID = \"fmS-eE-nne\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الخزن الافتراضي:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Výchozí úložiště" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Standardspeicherort:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Default Storage:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Almacenamiento por defecto:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Stockage par défaut:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אחסון ברירת מחדל:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डिफ़ॉल्ट संग्रहण:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Storage predefinito:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "デフォルトストレージ:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "기본 저장소:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Standaard opslag:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Armazenamento padrão:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Armazenamento por defeito:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Хранилище по умолчанию:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Varsayılan Depolama:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розташування за замовчуванням:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "默认存储:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "預設儲存空間:" } } } }, "FQV-5x-ffs.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Zoom\"; ObjectID = \"FQV-5x-ffs\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تقريب" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přiblížení" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Zoomen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Zoom" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Zoom" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Zoom" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הגדל/הקטן" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "ज़ूम" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Zoom" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "拡大" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "확대" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zoom" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Zoom" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Zoom" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Увеличить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yakınlaştır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Наблизити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "缩放" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "縮放" } } } }, "frt-CF-STi.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Change\"; ObjectID = \"frt-CF-STi\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تغيير" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Změnit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ändern" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Change" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cambiar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Changer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cambia" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "변경" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verander" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Alterar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Alterar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Изменить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Değiştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Змінити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更改" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "變更" } } } }, "FS0-ZJ-WvJ.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Files Naming:\"; ObjectID = \"FS0-ZJ-WvJ\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تسمية الملفات:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Pojmenování souborů:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Benennung der Dateien:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Files Naming:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nombre de archivos:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nommage des fichiers:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שם קבצים:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ाइल नामकरण:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Denominazione dei file:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ファイル名:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "파일 이름 지정:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Bestandsnaamgeving:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nomenclatura dos arquivos:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nomenclatura de ficheiros:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Именование файлов:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dosya Adlandırma:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Іменування файлів:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "文件命名中:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "檔案命名:" } } } }, "fsF-5N-tWg.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Made with ❤️ in Ukraine 🇺🇦 \"; ObjectID = \"fsF-5N-tWg\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مصنوع من ❤️ في أوكرانيا 🇺🇦 " } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vytvořeno s ❤️ v Ukrajině 🇺🇦 " } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Hergestellt mit ❤️ in der Ukraine 🇺🇦" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Made with ❤️ in Ukraine 🇺🇦 " } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Hecho con ❤️ en Ucrania 🇺🇦 " } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Fait avec ❤️ en Ukraine 🇺🇦 " } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נבנה ב-❤️ באוקראינה 🇺🇦" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "यूक्रेन 🇺🇦 में ❤️ के साथ बनाया गया" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Realizzato con ❤️ in Ucraina 🇺🇦 " } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ウクライナで❤️を使って作られました🇺🇦" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "우크라이나에서 ❤️으로 만든 🇺🇦" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Gemaakt met ❤️ in Ukraine 🇺🇦 " } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Feito com ❤️ na Ucrânia 🇺🇦" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Feito com ❤️ na Ucrânia 🇺🇦" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сделано с ❤️ в Украине 🇺🇦" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ukrayna'da ❤️ ile yapıldı 🇺🇦" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зроблено з ❤️ в Україні 🇺🇦" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "乌克兰 ❤️ 制造 🇺🇦 " } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "來自烏克蘭的 ❤️ 製作" } } } }, "fzU-ZR-Ubv.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Rename Folder\"; ObjectID = \"fzU-ZR-Ubv\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تسمية المجلد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přejmenovat složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner umbenennen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Rename Folder" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Renombar carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Renommer le dossier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה שם תיקיה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ोल्डर का नाम बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォルダの名称変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "폴더 이름 변경" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hernoem map" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Renomear pasta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomear pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать директорию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasörü Yeniden Adlandır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейменувати директорію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重命名文件夹" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重新命名資料夾" } } } }, "fzY-c6-DaK.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Auto-lock for Encrypted Notes:\"; ObjectID = \"fzY-c6-DaK\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "القفل التلقائي للملاحظات المشفرة:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Automaticky zamykat šifrované poznámky:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Automatische Sperre für verschlüsselte Notizen:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Auto-lock for Encrypted Notes:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear autómaticamente las notas encriptadas:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Verrouiller automatiquement les notes chiffrées :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נעילה אוטומטית לפתקים מוצפנים:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "एन्क्रिप्टेड नोट्स के लिए स्वतः लॉक:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Blocco automatico per note cifrate:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ロックされたノートを自動で閉じる:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "암호화된 노트 자동 잠금:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Automatisch vergrendelen voor versleutelde notities:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear automaticamente notas criptografadas:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear automático para notas criptografadas:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Автоблокировка:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Şifreli Notlar için Otomatik Kilit:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Авто-блокування:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "自动锁定加密的笔记:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "加密筆記自動鎖定:" } } } }, "g5W-YF-jAC.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Scan inline tags\"; ObjectID = \"g5W-YF-jAC\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مسح العلامات المضمنة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Detekovat značky v textu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Scan inline tags" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Scan inline tags" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Escanear etiquetas automáticamente" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Balayer les tags en ligne" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "סרוק תגים מוטבעים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इनलाइन टैग स्कैन करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Scansiona i tag in linea" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インラインタグをスキャンする" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "인라인 태그 스캔" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Inline-tags scannen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Escanear tags inline" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Examinar etiquetas em linha" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сканировать встроенные теги" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Satır içi etiketleri tarayın" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Cканувати вбудовані теги" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "扫描内联标签" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "掃描行內標籤" } } } }, "gbE-yH-ECm.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Save clipboard shortcut:\"; ObjectID = \"gbE-yH-ECm\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حفظ اختصار الحافظة:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vytvořit ze schránky:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Aus Zwischenablage kopieren:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Save clipboard shortcut:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "F. rápida guardar portapapeles:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sauvegarder le presse-papier :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "קיצור דרך לשמור את לוח העריכה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्लिपबोर्ड सहेजें शॉर्टकट:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Salva appunti in una nuova nota:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "クリップボードから新規作成:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "클립보드 저장 단축키:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Opslaan klembord-snelkoppeling" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Salvar atalho na área de transferência" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Guardar no 'clipboard':" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сохранить из буфера:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Panoya kaydetme kısayolu:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Швидке збереження:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "保存剪贴板快捷方式:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "儲存剪貼簿快速鍵:" } } } }, "Gbf-V7-5Ra.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"seconds\"; ObjectID = \"Gbf-V7-5Ra\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ثواني" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "sekund" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "seconds" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "seconds" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "seconds" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "seconds" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שניות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "seconds" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "seconds" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "seconds" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "seconds" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "seconds" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "segundos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "seconds" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "секунды" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "saniyeler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "seconds" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "秒" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "秒" } } } }, "GCG-C6-9cg.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Modification date\"; ObjectID = \"GCG-C6-9cg\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تاريخ التعديل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Datum změny" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Änderungsdatum" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Modification date" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Fecha de modificación" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Date de modification" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תאריך שינוי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सुधार की तारीख" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Data di modifica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "変更日" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "수정일" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wijzigingsdatum" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Data de modificação" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Data de modificação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Дата модификации" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Değişiklik tarihi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Датою модифікації" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "修改日期" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "修改日期" } } } }, "GcP-oI-0b4.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Cancel\"; ObjectID = \"GcP-oI-0b4\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الغاء" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zrušit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Abbrechen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Cancel" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cancelar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Annuler" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ביטול" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "रद्द करे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Annulla" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "キャンセル" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "취소" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Annuleer" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Cancelar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Cancelar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Отменить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İptal" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скасувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "取消" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "取消" } } } }, "GEO-Iw-cKr.title" : { "comment" : "Class = \"NSMenu\"; title = \"Format\"; ObjectID = \"GEO-Iw-cKr\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تنسيق" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Formát" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Format" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Format" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Formato" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Format" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פורמט" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रारूप" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Formato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォーマット" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "형식" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Formaat" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Formato" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Formato" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Форматирование" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Biçim" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Форматування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "格式" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "格式" } } } }, "gFA-SA-v9T.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"FSNotes Server\"; ObjectID = \"gFA-SA-v9T\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "خادم FSNotes" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes server" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes Server" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "FSNotes Server" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Servidor de FSNotes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Serveur FSNotes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes שרת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes सर्वर" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Server FSNotes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes サーバー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes 서버" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes-server" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Servidor do FSNotes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Servidor FSNotes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сервер FSNotes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes Sunucu" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes сервер" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes 服务器" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes 伺服器" } } } }, "Gg5-NO-KIQ.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"None Selected\"; ObjectID = \"Gg5-NO-KIQ\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "None Selected " } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nic nevybráno" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Keine ausgewählt" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "None Selected" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ninguno Seleccionado" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "None Selected " } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "None Selected " } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोई भी नहीं चुना गया" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nessuno selezionato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "選択なし" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "선택되지 않음" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Geen geselecteerd" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nada selecionado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nenhum selecionado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Не выбрано" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seçilmedi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не вибрано" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无选择" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "未選取" } } } }, "ghI-ln-bql.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Web:\"; ObjectID = \"ghI-ln-bql\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Web:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Web:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Web:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Web:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Web:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Web:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Web:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Web:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Web:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Web:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Web:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Web:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Web:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Веб:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Веб:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "主页:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Web:" } } } }, "GhL-6I-558.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Header 4\"; ObjectID = \"GhL-6I-558\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عنوان ٤" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nadpis 4" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Header 4" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Header 4" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Encabezado 4" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Titre 4" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "כותרת 4" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हेडर 4" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Intestazione 4" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ヘッダー 4" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Header 4" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kop 4" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Cabeçalho 4" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Cabeçalho 4" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок 4" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlık 4" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок 4" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "4级标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "標題 4" } } } }, "goN-LO-L7l.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Reveal in Finder\"; ObjectID = \"goN-LO-L7l\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "needs_review", "value" : "فتح المجلد" } }, "cs" : { "stringUnit" : { "state" : "needs_review", "value" : "Zobrazit ve Finderu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner anzeigen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Reveal in Finder" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Localiser dans le Finder" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג ב-Finder" } }, "hi" : { "stringUnit" : { "state" : "needs_review", "value" : "Finder में दिखाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra nel Finder" } }, "ja" : { "stringUnit" : { "state" : "needs_review", "value" : "Finderに表示" } }, "ko" : { "stringUnit" : { "state" : "needs_review", "value" : "Finder에서 보기" } }, "nl-NL" : { "stringUnit" : { "state" : "needs_review", "value" : "Toon in Finder" } }, "pt-BR" : { "stringUnit" : { "state" : "needs_review", "value" : "Mostrar no Finder" } }, "pt-PT" : { "stringUnit" : { "state" : "needs_review", "value" : "Mostrar no Finder" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать в Finder" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Finder'da göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати у Finder" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在访达中显示" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示於 Finder" } } } }, "gra-E2-dYh.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Show in Finder\"; ObjectID = \"gra-E2-dYh\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فتح المجلد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit ve Finderu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner anzeigen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Show in Finder" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Localiser dans le Finder" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג ב-Finder" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Finder में दिखाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra nel Finder" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Finderに表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Finder에서 보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon in Finder" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar no Finder" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar no Finder" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать в Finder" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Finder'da göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати в Finder" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在访达中显示" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示於 Finder" } } } }, "GrP-W1-7ZQ.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Lock All Encrypted\"; ObjectID = \"GrP-W1-7ZQ\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "قفل جميع المشفرة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zamknout všechny šifrované" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Alle verschlüsselte sperren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Lock All Encrypted" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear todas las notas encriptadas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Tout verrouiller" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נעל פתקים מוצפנים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सभी एन्क्रिप्टेड लॉक करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Blocca tutte le note criptate" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ロックされたノートをすべて閉じる" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "모든 암호 노트 잠금" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Vergrendel alle versleutelde" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear todos os criptografados" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear Todos os Criptografados" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заблокировать все секретные" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tüm Şifrelenmişleri Kilitle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заблокувати всі секретні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "锁定所有加密的笔记" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "鎖定所有加密項目" } } } }, "gVA-U4-sdL.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Paste\"; ObjectID = \"gVA-U4-sdL\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "لصق" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vložit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einfügen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Paste" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Pegar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Coller" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הדבק" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पेस्ट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Incolla" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ペースト" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "붙여넣기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Plakken" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Colar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Colar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вставить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yapıştır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вставити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "粘贴" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "貼上" } } } }, "gWb-su-EdI.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"FSNotes\"; ObjectID = \"gWb-su-EdI\"; Note = \"#bc-ignore!\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "FSNotes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } } } }, "Gwp-vX-YaR.headerCell.title" : { "comment" : "Class = \"NSTableColumn\"; headerCell.title = \"Library\"; ObjectID = \"Gwp-vX-YaR\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "المكتبة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Knihovna" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Library" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Library" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Librería" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Bibliothèque" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Library" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पुस्तकालय" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Libreria" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ライブラリ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "라이브러리" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Bibliotheek" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Biblioteca" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Biblioteca" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Библиотека" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kütüphane" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Бібліотека" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "资源库" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "資料庫" } } } }, "gY8-zt-Iak.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Dark\"; ObjectID = \"gY8-zt-Iak\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "داكن" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Tmavý" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Dunkel" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Dark" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Oscuro" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sombre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חשוך" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Dark" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Scuro" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ダーク" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다크" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Donker" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Escuro" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Escuro" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Тёмная" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Karanlık" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Темна" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "深色" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "深色" } } } }, "gYQ-MC-Xwt.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Zoom Out\"; ObjectID = \"gYQ-MC-Xwt\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تصغير" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zmenšit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Verkleinern" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Zoom Out" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Alejar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dézoomer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "להקטין את התצוגה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "ज़ूम आउट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ingrandimento" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ズームアウト" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "축소" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Uitzoomen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Reduzir" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Reduzir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Уменьшить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Uzaklaştır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зменшити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "缩小" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "縮小" } } } }, "H8h-7b-M4v.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"View\"; ObjectID = \"H8h-7b-M4v\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عرض" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazení" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Darstellung" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "View" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Visualización" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Affichage" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תצוגה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "देखें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Vista" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Weergave" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Visualização" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Vista" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вид" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Görünüm" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вигляд" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "视图" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "檢視" } } } }, "Hdn-bm-lbP.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Reset Settings\"; ObjectID = \"Hdn-bm-lbP\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اعادة الضبط" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Resetovat nastavení" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einstellungen zurücksetzen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Reset Settings" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Restablecer la configuración" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Réinitialiser les paramètres" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אפס הגדרות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्राथमिकताएं रीसेट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ripristino delle impostazioni" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "設定のリセット" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "설정 초기화" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Instellingen resetten" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Redefinir configurações" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Repor definições" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сбросить настройки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ayarları sıfırla" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скинути налаштування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重置设置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重設設定" } } } }, "HFo-cy-zxI.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Show Spelling and Grammar\"; ObjectID = \"HFo-cy-zxI\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عرض التدقيق الإملائي والنحوي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit pravopis a gramatiku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Rechtschreibung und Grammatik einblenden" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Show Spelling and Grammar" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar ortografía y gramática" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher Orthographe et grammaire" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג איות ודקדוק" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वर्तनी और व्याकरण दिखाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra Ortografia e Grammatica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "スペルと文法を表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "맞춤법 및 문법 보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon Spelling en Grammatica" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar Ortografia e Gramática" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar Ortografia e Gramática" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать орфографию и грамматику" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yazım ve Dilbilgisini Göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати правопис та граматику" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "显示拼写和语法" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示拼字與文法" } } } }, "HFQ-gK-NFA.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Text Replacement\"; ObjectID = \"HFQ-gK-NFA\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "استبدال النص" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nahrazovat text" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Text ersetzen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Text Replacement" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Reemplazar texto" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Remplacement de texte" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מלל חלופי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पाठ प्रतिस्थापन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sostituzione Testo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ユーザー辞書" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "텍스트 대치" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Tekstvervanging" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Substituição de texto" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Substituição de Texto" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Замена текста" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Metin Değiştirme" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заміна тексту" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "文本替换" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "文字替代" } } } }, "HK7-Qq-ilB.title" : { "comment" : "Class = \"NSMenuItem\"; title = \".markdown\"; ObjectID = \"HK7-Qq-ilB\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "en" : { "stringUnit" : { "state" : "new", "value" : ".markdown" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : ".markdown" } } } }, "HmK-lp-ASv.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Open Note in New Window\"; ObjectID = \"HmK-lp-ASv\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "needs_review", "value" : "افتح في نافذة جديدة" } }, "cs" : { "stringUnit" : { "state" : "needs_review", "value" : "Otevřít v novém okně" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In neuem Fenster öffnen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Open Note in New Window" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Abrir en Nueva ventana" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir dans une nouvelle fenêtre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פתח בחלון חדש" } }, "hi" : { "stringUnit" : { "state" : "needs_review", "value" : "नई विंडो में खोलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apri in una nuova finestra" } }, "ja" : { "stringUnit" : { "state" : "needs_review", "value" : "新しいウィンドウで開きます" } }, "ko" : { "stringUnit" : { "state" : "needs_review", "value" : "새 창에서 열기" } }, "nl-NL" : { "stringUnit" : { "state" : "needs_review", "value" : "Notitie openen in nieuw venster" } }, "pt-BR" : { "stringUnit" : { "state" : "needs_review", "value" : "Abrir em nova janela" } }, "pt-PT" : { "stringUnit" : { "state" : "needs_review", "value" : "Abrir em nova janela" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открыть в новом окне" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni Pencerede Aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відкрити у новому вікні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在新窗口中打开" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在新視窗開啟筆記" } } } }, "HPa-9J-5dS.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Reset\"; ObjectID = \"HPa-9J-5dS\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إعادة ضبط" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Resetovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Zurücksetzen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Reset" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Reiniciar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Réinitialiser" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אִתחוּל" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "रीसेट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ripristina" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リセット" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "초기화" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Resetten" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Resetar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Redefinir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сбросить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sıfırla" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скинути" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重設" } } } }, "hPY-aY-QOE.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Search and Create\"; ObjectID = \"hPY-aY-QOE\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "بحث وانشاء" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat a vytvořit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Suchen und ersetzen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Search and Create" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Buscar y crear" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Rechercher et créer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חיפוש יצירה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खोजें और बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cerca e crea" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "検索または新規作成" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "검색 및 추가" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zoek en creëer" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar e criar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar e criar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Найти и создать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ara ve oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Знайти або створити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "搜索或创建(按下回车即可创建)" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "搜尋並新增" } } } }, "hQb-2v-fYv.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Smart Quotes\"; ObjectID = \"hQb-2v-fYv\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اقتباسات ذكية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Inteligentní uvozovky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Intelligente Anführungszeichen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Smart Quotes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Comillas tipográficas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Guillemets intelligents" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מרכאות חכמות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्मार्ट उद्धरण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Virgolette Smart" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "スマート引用符" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "스마트 인용" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Slimme Aanhalingstekens" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Aspas inteligentes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Aspas Inteligentes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Смарт-кавычки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Akıllı Alıntılar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розумні лапки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "智能引号" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "智慧型引號" } } } }, "hqQ-4C-VJL.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"WikiLinks\"; ObjectID = \"hqQ-4C-VJL\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ويكيلينكس" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "WikiLinks" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "WikiLinks" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "WikiLinks" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "WikiLinks" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "WikiLinks" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "WikiLinks" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "WikiLinks" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "WikiLinks" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "WikiLinks" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "위키 링크" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "WikiLinks" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "WikiLinks" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "WikiLinks" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Викиссылки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Wiki Bağlantıları" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вікіпосилання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "维基链接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "維基連結" } } } }, "Hqu-Sx-xgo.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Move Up in the Notes List\"; ObjectID = \"Hqu-Sx-xgo\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تحرك لأعلى في قائمة الملاحظات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Posunout nahoru v seznamu poznámek" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In der Notizliste aufsteigen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Move Up in the Notes List" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mover arriba en la lista de notas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Note précédente" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עבור למעלה ברשימת הפתקים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट्स सूची में ऊपर जाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Muovi in Alto nella Lista Note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "上に移動" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "메모 목록에서 위로 이동" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Omhoog gaan in de notitielijst" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mover para cima lista de notas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Subir na lista de Notas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Перейти вверх в списке заметок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notlar Listesinde Yukarı Çık" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейти вгору у списку нотаток" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在笔记列表中上移" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在筆記清單中上移" } } } }, "HyV-fh-RgO.title" : { "comment" : "Class = \"NSMenu\"; title = \"View\"; ObjectID = \"HyV-fh-RgO\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عرض" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazení" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Darstellung" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "View" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Visualización" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Affichage" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תצוגה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "देखें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Vista" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Weergave" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Visualização" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Vista" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вид" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Görünüm" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вигляд" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "视图" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "檢視" } } } }, "hz2-CU-CR7.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Check Document Now\"; ObjectID = \"hz2-CU-CR7\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فحص المستند الآن" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zkontrolovat dokument" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Dokument jetzt prüfen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Check Document Now" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Comprobar documento ahora" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Vérifier le document maintenant" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "בדוק את המסמך כעט" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "दस्तावेज़ अभी जांचें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Controlla ora il documento" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "今すぐドキュメントをチェック" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "지금 도큐멘트 검사" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Controleer Document Nu" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Verificar documento agora" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Verificar Documentos Agora" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Проверить документ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Belgeyi Şimdi Kontrol Et" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перевіряти документ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "立即检查文档" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "立即檢查文件" } } } }, "hz9-B4-Xy5.title" : { "comment" : "Class = \"NSMenu\"; title = \"Services\"; ObjectID = \"hz9-B4-Xy5\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الخدمات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Služby" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Services" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Services" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Servicios" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Services" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שירותים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सेवाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Servizi" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サービス" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "서비스" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Services" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Serviços" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Serviços" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Услуги" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Hizmetler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сервіси" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "服务" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "服務" } } } }, "i2S-pt-KQH.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Trash:\"; ObjectID = \"i2S-pt-KQH\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الملهملات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Koš" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Papierkorb:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Trash:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Papelera:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Corbeille:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פח אשפה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कूडा:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cestino:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ゴミ箱:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "휴지통:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Prullenmand:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Lixo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Lixo:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Корзина:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Çöp:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сміття:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "废弃:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "垃圾桶:" } } } }, "IHG-4U-pF1.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Inbox\"; ObjectID = \"IHG-4U-pF1\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الوارد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Příchozí" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Posteingang" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Inbox" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Entrada" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Boîte de réception" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תיבת דואר נכנס" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इनबॉक्स" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "In Entrata" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "未整理" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "받은 편지함" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Postvak In" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Caixa de entrada" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Caixa de entrada" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Входящие" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Gelen kutusu" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вхідні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "收集箱" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "收件匣" } } } }, "iIP-wI-OaZ.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Commit & Push\"; ObjectID = \"iIP-wI-OaZ\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "needs_review", "value" : "تخزين النسخ الاحتياطي" } }, "cs" : { "stringUnit" : { "state" : "needs_review", "value" : "Zálohovat nyní" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Commit & Push" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Almacenamiento de respaldo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sauvegarder le stockage" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גבה עכשיו" } }, "hi" : { "stringUnit" : { "state" : "needs_review", "value" : "अभी बैकअप लें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Salva archivio" } }, "ja" : { "stringUnit" : { "state" : "needs_review", "value" : "今すぐバックアップ" } }, "ko" : { "stringUnit" : { "state" : "needs_review", "value" : "저장소 백업" } }, "nl-NL" : { "stringUnit" : { "state" : "needs_review", "value" : "Back-up opslag" } }, "pt-BR" : { "stringUnit" : { "state" : "needs_review", "value" : "Fazer backup agora" } }, "pt-PT" : { "stringUnit" : { "state" : "needs_review", "value" : "Cópia de segurança de armazenamento" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать резервную копию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Commit & Push" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "提交並推送" } } } }, "IlF-tS-GFG.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Lock on screen saver activated\"; ObjectID = \"IlF-tS-GFG\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "قفل عند تفعيل شاشة التوقف" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zamknout při spořiči obrazovky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sperren, wenn der Bildschirmschoner aktiv" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Lock on screen saver activated" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear cuando se active el salvapantallas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Verrouiller à l'activation de l'économiseur d'écran" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נעל בהפעלת שומר מסך" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्क्रीन सेवर सक्रिय होने पर लॉक करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Blocca a screen saver attivato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "スクリーンセーバー起動時" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "화면 보호기 실행시 잠금" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Vergrendel bij activering schermbeveiliging" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear quando o protetor de tela ativar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear quando o protector de ecrã activar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заблокировать при включении скринсейвера" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ekran koruyucusu etkinken kilitlendi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Блокувати при активації скрінсейвера" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "系统激活屏幕保护后自动锁定" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "啟動螢幕保護程式時鎖定" } } } }, "iNK-dg-n5E.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"New Password:\"; ObjectID = \"iNK-dg-n5E\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "كلمة المرور الجديدة:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nové heslo:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neue Passwort:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "New Password:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Contraseña nueva:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nouveau mot de passe :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "סיסמה חדשה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नया पासवर्ड:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuova password:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新しいパスワード:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새 비밀번호:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Nieuw wachtwoord:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nova senha:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nova palavra-passe:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Новый пароль:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni şifre :" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Новий пароль:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "新的密码:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "新密碼:" } } } }, "ioc-u5-XRA.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Hint:\"; ObjectID = \"ioc-u5-XRA\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تلميحة :" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nápověda:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Hint:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Hint:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Indicación:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Pense-bête :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "רמז:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संकेत:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Suggerimento:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ヒント:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "힌트:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hint:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Dica:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Dica:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Подсказка:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İpucu:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Підказка:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "提示:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "提示:" } } } }, "IPu-Ll-IBE.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Shift Right\"; ObjectID = \"IPu-Ll-IBE\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تحول اليمين" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Posunout doprava" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Text nach rechts bewegen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Shift Right" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Tabular a la derecha" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Décaler vers la droite" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הזחה ימינה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "दांयी ओर शिफ्ट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sposta a destra" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "右にシフト" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "들여쓰기 추가" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verschuif Rechts" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mover para esquerda" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Deslocar à Direita" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сдвиг вправо" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sağa kaydır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зміщення вправо" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "右移" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "向右位移" } } } }, "iQ9-lm-lFb.placeholderString" : { "comment" : "Class = \"NSTextFieldCell\"; placeholderString = \"no key\"; ObjectID = \"iQ9-lm-lFb\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "bez klíče" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "no key" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोई चाबी नहीं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "нет ключа" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "anahtar yok" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "no key" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無金鑰" } } } }, "IQv-IB-iLA.title" : { "comment" : "Class = \"NSWindow\"; title = \"FSNotes\"; ObjectID = \"IQv-IB-iLA\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "FSNotes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } } } }, "iUm-Zm-zeg.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Line Spacing:\"; ObjectID = \"iUm-Zm-zeg\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تباعد الاسطر" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Rozestup řádků:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Zeilenabstand:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Line Spacing:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Espaciado de línea:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Espacement des lignes :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מרווח בין שורות:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पंक्ति रिक्ति:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Interlinea:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "行間隔:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "줄 간격:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Regelafstand:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Espaçamento da linha:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Espaçamento de linhas:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Межстрочный интервал:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Satır Aralığı:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Міжрядковий інтервал:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "行间距:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "行距:" } } } }, "IWL-lp-G1M.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Format: Untitled Note\"; ObjectID = \"IWL-lp-G1M\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "التنسيق: Untitled Note" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Formát: Poznámka bez názvu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Format: Untitled Note" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Format: Untitled Note" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Formato: Nota sin título" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Format: Untitled Note" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פורמט: Untitled Note" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रारूप: शीर्षक रहित नोट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Formato: Nota senza titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォーマット: 名称未設定" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "체재: Untitled Note" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Format: Untitled Note" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Formato: nota sem título" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Formato: Nota sem título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Формат: Untitled Note" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Biçim: Başlıksız Not" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Формат: Нотатка без назви" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "格式: 无标题笔记" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "格式:無標題筆記" } } } }, "Izt-6v-pKO.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Line Width:\"; ObjectID = \"Izt-6v-pKO\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عرض الخط:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Šířka textu:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Linienbreite:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Line Width:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ancho de línea:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Largeur de ligne :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "רוחב שורות:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पंक्ति चौडाई:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Larghezza linea:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "行幅:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "줄 간격:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Lijnbreedte:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Largura da linha" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Largura da linha:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ширина строки:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Çizgi Genişliği:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ширина рядка:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "行宽:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "行寬:" } } } }, "jFN-Dp-LON.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Pin/Unpin\"; ObjectID = \"jFN-Dp-LON\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "دبوس / إلغاء التثبيت" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Připnout/odepnout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Fixieren/Loslösen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Pin/Unpin" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Anclar/Desanclar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Épingler/Désépingler" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצמד/בטל הצמדה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पिन/अनपिन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Fissa/sblocca" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ピンで固定/ピン固定を解除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "고정/고정 해제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Pin/VerwijderPin" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Fixar/Desafixar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Fixar/soltar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Прикрепить/открепить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sabitle/Sabitlemeyi kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Закріпити/відкріпити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "置顶/取消置顶" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "釘選/取消釘選" } } } }, "jLq-8F-n0X.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Rename\"; ObjectID = \"jLq-8F-n0X\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اعادة تسمية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přejmenovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Umbenennen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Rename" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Renombrar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Renommer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה שם" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नाम बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "名称変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이름 변경" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hernoem" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Renomear" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomear" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeniden isimlendir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейменувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重命名" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重新命名" } } } }, "jQ1-6K-9ar.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"🔗fsnotes/contributors\"; ObjectID = \"jQ1-6K-9ar\"; Note = \"#bc-ignore!\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "🔗fsnotes/contributors" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/katkıda bulunanlar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "🔗fsnotes/contributors" } } } }, "jrj-ea-xmm.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Visibility:\"; ObjectID = \"jrj-ea-xmm\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الرؤية:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Viditelnost:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sichtbarkeit:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Visibility:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Visibilidad:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Visibilité :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ראות:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "दृश्यता:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Visibilità:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "視認性:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "가시성:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zichtbaarheid:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Visibilidade:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Visibilidade:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Видимость:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Görünürlük:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видимість:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "可见性:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "能見度:" } } } }, "jRm-BN-BNK.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Header 1\"; ObjectID = \"jRm-BN-BNK\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عنوان ١" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nadpis 1" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Header 1" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Header 1" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Encabezado 1" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Titre 1" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "כותרת 1" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हेडर 1" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Intestazione 1" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ヘッダー 1" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Header 1" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kop 1" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Cabeçalho 1" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Cabeçalho 1" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок 1" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlık 1" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок 1" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "1级标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "標題 1" } } } }, "jRr-Ih-RYc.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"**\"; ObjectID = \"jRr-Ih-RYc\";", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "**" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "**" } } }, "shouldTranslate" : false }, "juB-By-EsE.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Set\"; ObjectID = \"juB-By-EsE\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تعيين" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nastavit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Satz" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Set" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Establecer" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Régler" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מַעֲרֶכֶת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सेट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Impostare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Set" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "세트" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Set" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Definir" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Definir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Выбрать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ayarla" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вибрати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "设置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "設定" } } } }, "jxT-CU-nIS.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Format\"; ObjectID = \"jxT-CU-nIS\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تنسيق" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Formát" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Format" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Format" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Formato" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Format" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פורמט" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रारूप" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Formato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォーマット" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "형식" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Formaat" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Formato" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Formato:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Форматирование" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Biçim" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Форматування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "格式" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "格式" } } } }, "K4N-le-FPU.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Delete Web Page\"; ObjectID = \"K4N-le-FPU\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حذف صفحة الويب" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Smazat webovou stránku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Geteilte löschen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Delete Web Page" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar página web" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer la page Web" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מחק דף אינטרנט" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब पेज हटाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Elimina pagina web" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Delete Web Page" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "웹 페이지 삭제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Webpagina verwijderen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Deletar página Web" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Excluir página da Web" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить веб-страницу" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web sayfasını Sil" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити веб-сторінку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除网页" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "刪除網頁" } } } }, "k32-YP-yyI.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Italic\"; ObjectID = \"k32-YP-yyI\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مائل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kurzíva" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Kursiv" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Italic" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cursiva" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Italique" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נטוי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "तिरछा" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Italic" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "イタリック" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이텔릭체" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Cursief" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Itálico" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Itálico" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Курсив" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İtalik" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Курсив" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "斜体" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "斜體" } } } }, "KaC-Mz-siK.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Password:\"; ObjectID = \"KaC-Mz-siK\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "كلمة المرور:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Heslo:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passwort:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Password:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Contraseña:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mot de p. :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "סיסמה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासवर्ड:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Parola d'ordine:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスワード:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "암호:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wachtwoord:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Senha:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Passe:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пароль:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Şifre:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Пароль:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "密码:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "密碼:" } } } }, "kbK-Um-cQk.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Use first line as title\"; ObjectID = \"kbK-Um-cQk\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "استخدم اول سطر كعنوان" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Použít první řádek jako nadpis" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erste Zeile als Titel verwenden" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Use first line as title" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Utilizar la primera línea como título" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Utiliser la première ligne comme titre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "השתמש בשורה הראשונה ככותרת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रथम पंक्ति को शीर्षक के रूप में उपयोग करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Usa la prima riga come titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "最初の行をタイトルとして使用" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "첫 줄을 제목으로 사용" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Gebruik eerste regel als titel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Usar primeira linha como título" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Utilizar a primeira linha como título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Первая строка как заголовок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İlk satırı başlık olarak kullanın" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перший рядок як заголовок" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "将内容第一行提取为标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "使用第一行作為標題" } } } }, "Kd2-mp-pUS.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Show All\"; ObjectID = \"Kd2-mp-pUS\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عرض الكل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit vše" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Alle anzeigen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Show All" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar todo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Tout afficher" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג הכל" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सब दिखाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra Tutto" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "すべてを表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "모두 보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon alle" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar todos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar Todos" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать все" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Hepsini göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати все" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "显示全部" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示全部" } } } }, "kg2-3u-V99.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Lock/Unlock\"; ObjectID = \"kg2-3u-V99\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فقل/فتح الفقل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zamknout/odemknout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sperren/Entsperren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Lock/Unlock" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear/desbloquear" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Verrouiller/Déverrouiller" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נעל/פתח" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लॉक/अनलॉक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Blocca/sblocca" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ロック/ロックの削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "잠금/잠금 해제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Vergrendel/ontgrendel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear/desbloquear" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear/desbloquear" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заблокировать/разблокировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kilitle/Kilidi Aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заблокувати/розблокувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "锁定/解锁" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "鎖定/解鎖" } } } }, "kIy-TN-XMS.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Close\"; ObjectID = \"kIy-TN-XMS\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اغلاق" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zavřít" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Schließen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Close" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cerrar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Fermer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "סגור" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बंद करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Chiudi" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "閉じる" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "닫기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Sluit" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Fechar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Fechar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Закрыть" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kapat" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Закрити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "关闭" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "關閉" } } } }, "KIz-OO-IQT.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Preview MathJax\"; ObjectID = \"KIz-OO-IQT\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "معاينة MathJax " } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Náhled MathJax" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Vorschau MathJax" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Preview MathJax" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Previsualizar MathJax" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aperçu MathJax" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תצוגת MathJax" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पूर्वावलोकन MathJax" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Anteprima MathJax" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "MathJaxをプレビュー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "미리보기 MathJax" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voorvertoning MathJax" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pré-visualização do MathJax" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pré-visualizar MathJax" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Предпросмотр MathJax" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "MathJaxı Önizleyin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Попередній перегляд MathJax" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "预览 MathJax" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "預覽 MathJax" } } } }, "kqJ-Fr-EeB.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"1\"; ObjectID = \"kqJ-Fr-EeB\"; Note = \"#bc-ignore!\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "1" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "1" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "1" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "1" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "1" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "1" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "1" } } } }, "KST-y3-KvM.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Allow Touch ID to unlock notes\"; ObjectID = \"KST-y3-KvM\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "السماح لبصمة الاصبع لفتح الملاحظات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Povolit Touch ID pro odemykání poznámek" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "TouchID erlauben zum Entsperren von Notizen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Allow Touch ID to unlock notes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Permitir desbloquar las notas con Touch ID" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déverrouillage de notes par Touch ID" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אפשר ל-Touch ID לפתוח פתקים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टच आईडी को नोट्स अनलॉक करने की अनुमति दें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Consenti a Touch ID di sbloccare le note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Touch IDを使ってロックされたノートを表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID를 이용하여 노트 잠금 해제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Sta Touch ID toe om notities te ontgrendelen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Permitir Touch ID para bloquear notas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Permitir desbloquear notas com Touch ID" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Разблокировка Touch ID" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notların kilidini açmak için Touch ID'ye izin verin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Використовувати Touch ID " } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "允许使用 Touch ID 解锁笔记" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "允許使用 Touch ID 解鎖筆記" } } } }, "L20-FT-VZz.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Origin for main project:\"; ObjectID = \"L20-FT-VZz\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Origin للمشروع الرئيسي:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Origin pro hlavní projekt:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Origin for main project:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Origin for main project:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Origin para el proyecto principal:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Origin pour le projet principal:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Origin לפרויקט הראשי:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मुख्य प्रोजैक्ट का उद्गम स्थान:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Origin per il progetto principale:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Origin メインプロジェクト用:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Origin 메인 프로젝트를 위해:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Origin voor hoofdproject::" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Origem do projeto principal:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Origin para projeto principal:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удаленный репозиторий (origin):" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ana projenin kökeni:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Remote origin основного проекту:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Origin 主要项目:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "主要專案來源:" } } } }, "ldu-U8-PD1.title" : { "comment" : "Class = \"NSWindow\"; title = \"Preferences\"; ObjectID = \"ldu-U8-PD1\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "التفضيلات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Předvolby" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einstellungen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Preferences" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Preferencias" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Préférences" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העדפות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्राथमिकताएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Preferenze" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "環境設定…" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "환경설정" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voorkeuren" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Preferências" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Preferências" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Окно" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tercihler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вікно" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "偏好设置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "偏好設定" } } } }, "LFw-De-3DP.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Actual Size\"; ObjectID = \"LFw-De-3DP\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الحجم الأصلي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Skutečná velikost" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Tatsächliche Größe" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Actual Size" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Tamaño real" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Taille réelle" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גודל אמיתי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वास्तविक आकार" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Dimensione reale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "実寸" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "실제 크기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Werkelijke grootte" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tamanho real" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tamanho real" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Фактический размер" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Gerçek Boyut" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Фактичний розмір" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "实际尺寸" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "實際大小" } } } }, "lhy-wX-Q4T.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Reset:\"; ObjectID = \"lhy-wX-Q4T\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إعادة ضبط:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Resetovat:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Zurücksetzen:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Reset:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Restablecer:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Réinitialiser :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אִתחוּל:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "रीसेट करें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Azzeramento:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リセット" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "초기화" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Reset:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Redefinir:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Repor:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сброс:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sıfırla:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скинути:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重設:" } } } }, "LKs-o1-uhL.title" : { "comment" : "Class = \"NSMenu\"; title = \"Share\"; ObjectID = \"LKs-o1-uhL\";", "extractionState" : "extracted_with_value", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", "value" : "Teilen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Share" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Compartir" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Partager" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "साझा करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Condividi" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Поделиться" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Paylaş" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Поділитися" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "分享" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "分享" } } } }, "Lmy-lE-9MZ.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Header 3\"; ObjectID = \"Lmy-lE-9MZ\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عنوان ٣" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nadpis 3" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Header 3" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Header 3" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Encabezado 3" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Titre 3" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "כותרת 3" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हेडर 3" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Intestazione 3" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ヘッダー 3" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Header 3" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kop 3" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Cabeçalho 3" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Cabeçalho 3" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок 3" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlık 3" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок 3" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "3级标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "標題 3" } } } }, "LPT-uW-BtF.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Title\"; ObjectID = \"LPT-uW-BtF\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عنوان" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nadpis" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Title" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Title" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Título" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Titre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "כותרת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タイトル" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "제목" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Titel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Título" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlık" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "標題" } } } }, "ltm-qj-nke.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Images Width:\"; ObjectID = \"ltm-qj-nke\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عرض الصور:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Šířka obrázků:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bilder Breite:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Images Width:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ancho de imágenes:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Largeur des images :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "רוחב תמונות:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "छवियाँ चौड़ाई:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Larghezza delle immagini:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "画像の幅:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이미지 가로폭:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Afbeeldingen breedte:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Largura das imagens:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Largura de imagens:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ширина изображений:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Resim Genişliği:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ширина зображення:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "图像宽度:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "圖片寬度:" } } } }, "LTw-w4-tEo.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Bold\"; ObjectID = \"LTw-w4-tEo\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "سميك" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Tučné" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Fett" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Bold" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Negrita" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Gras" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עבה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बोल्ड" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Grassetto" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ボールド" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "볼드" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Vet" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Negrito" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Realçado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Жирный" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kalın" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Жирний" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "粗体" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "粗體" } } } }, "LZ2-uY-6YK.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Private key\"; ObjectID = \"LZ2-uY-6YK\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Soukromý klíč" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Private key" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "निजी कुंजी" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Chave privada" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Закрытый ключ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Özel anahtar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "私密金鑰" } } } }, "M29-fW-FJa.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Hide FSNotes when activating another application\"; ObjectID = \"M29-fW-FJa\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إخفاء FSNotes عند تفعيل تطبيق آخر" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Skrýt FSNotes při aktivaci jiné aplikace" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes ausblenden, wenn andere Anwendung aktiv" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Hide FSNotes when activating another application" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar FSNotes cuando no sea la aplicación activa" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Masquer FSNotes à l'activation d'une autre app" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסתר את FSNotes כשמפעילים אפליקציה אחרת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "किसी अन्य एप्लिकेशन को सक्रिय करते समय FSNotes छिपाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nascondi FSNotes quando usi altre app" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "他のアプリケーションがアクティブ時にFSNotesを非表示にする" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다른 애플리케이션 활성화 시 FSNotes 숨기기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verberg FSNotes bij het activeren van een andere applicatie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar o FSNotes ao ativar outro aplicativo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar FSNotes quando há outra aplicação activa" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Прятать окно если активно другое приложение" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başka bir uygulamayı etkinleştirirken FSNotes'u gizle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сховати при активуванні іншої программи" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "激活其他应用时自动隐藏 FSNotes" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "啟動其他應用程式時隱藏 FSNotes" } } } }, "MCR-1v-3Wc.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"TextBundle\"; ObjectID = \"MCR-1v-3Wc\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حزمة النص" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "TextBundle" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "텍스트 번들" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "MetinPaketi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } } } }, "ME1-My-h4q.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"atom-one\"; ObjectID = \"ME1-My-h4q\";", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "atom-one" } }, "ja" : { "stringUnit" : { "state" : "needs_review", "value" : "Atom One Light" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "atom-one" } } }, "shouldTranslate" : false }, "mK0-16-Mxo.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"4 Spaces\"; ObjectID = \"mK0-16-Mxo\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "4 Spaces" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "4 mezery" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "4 Spaces" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "4 Spaces" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "4 Spaces" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "4 Spaces" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "4 Spaces" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "4 स्थान" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "4 Spaces" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "4 スペース" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "4 Spaces" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "4 Spaces" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "4 espaços" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "4 Spaces" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "4 пробела" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "4 Boşluk" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "4 пробіли" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "缩进 4 个空格" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "4 個空格" } } } }, "mK6-2p-4JG.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Check Grammar With Spelling\"; ObjectID = \"mK6-2p-4JG\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تحقق من القواعد مع التدقيق الإملائي\n" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kontrolovat gramatiku a pravopis" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Grammatik und Rechtschreibung prüfen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Check Grammar With Spelling" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Comprobar gramática con la ortografía" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Vérifier la grammaire avec l'orthographe" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "בדוק דקדוק ביחד עם איות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वर्तनी के साथ व्याकरण की जाँच करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Controlla Ortografia e Grammatica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "スペルと一緒に文法をチェック" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "맞춤법 및 문법 검사" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Controleer grammatica tegelijk met spelling" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Verificar Ortografia e Gramática" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Verificar gramática com ortografia" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Проверить грамматику с орфографией" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yazımla Dilbilgisini Kontrol Et" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перевіряти граматику та правопис" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "检查拼写和语法" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "檢查拼字與文法" } } } }, "MlK-p2-CxF.title" : { "comment" : "Class = \"NSMenu\"; title = \"Window\"; ObjectID = \"MlK-p2-CxF\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نافذة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Okno" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Fenster" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Window" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ventana" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Fenêtre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חלון" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "विंडो" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Finestra" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ウィンドウ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "윈도우" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Venster" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Janela" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Janela" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Окно" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Pencere" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вікно" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "窗口" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "視窗" } } } }, "mr6-e8-9hL.placeholderString" : { "comment" : "Class = \"NSTextFieldCell\"; placeholderString = \"https://example.com/\"; ObjectID = \"mr6-e8-9hL\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "https://priklad.com/" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "https://example.com/" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "https://domainadi.com/" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "https://example.com/" } } } }, "mvt-gI-iG0.title" : { "comment" : "Class = \"NSViewController\"; title = \"Advanced\"; ObjectID = \"mvt-gI-iG0\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "متقدمة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Pokročilé" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erweitert" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Advanced" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Avanzado" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Avancé" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מתקדם" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "उन्नत" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Avanzate" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "詳細" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "고급" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Geavanceerd" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Avançado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Avançado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Расширенные" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Gelişmiş" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розширені" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "高级" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "進階" } } } }, "mXw-d9-js5.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Container:\"; ObjectID = \"mXw-d9-js5\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الحاوية:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kontejner:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Container:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Container:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Contenedor:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Conteneur :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מיכל:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कंटेनर:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Container:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "コンテナ:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "컨테이너:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Container:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Container:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Contentor:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Контейнер:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Konteyner:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Контейнер:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "容器:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "容器:" } } } }, "n0M-SN-fzg.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Toggle Todo\"; ObjectID = \"n0M-SN-fzg\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تبديل Todo" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přepnout úkol" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Auf Todo umschalten" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Toggle Todo" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Insertar Pendiente" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Cocher la tâche" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "החלף מצב מטלה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टॉगल टूडू" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Passa a Da Fare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タスクの切り替え" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "할 일 토글" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wissel Te Doen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Alterar 'A fazer'" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Alternar tarefa" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переключить Todo" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Geçiş Yap" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Завдання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "切换待办事项" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "切換待辦事項狀態" } } } }, "N8h-ep-HFG.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Clickable links\"; ObjectID = \"N8h-ep-HFG\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Clickable links" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Klikatelné odkazy" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Clickable links" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Clickable links" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Clickable links" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Liens cliquables" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "קישורים ללחיצה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्लिक करने योग्य लिंक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Clickable links" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "クリック可能なリンク" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Clickable links" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Clickable links" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Links clicáveis" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Clickable links" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Кликабельные ссылки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tıklanabilir bağlantılar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Клікабельні посилання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "可点击的链接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "可點擊連結" } } } }, "nBn-aV-7mt.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Change\"; ObjectID = \"nBn-aV-7mt\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تغيير" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Změnit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bearbeiten" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Change" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cambiar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Changer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cambia" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "변경" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verander" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Alterar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Alterar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Изменить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Değiştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Змінити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更改" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "變更" } } } }, "NcS-0N-6uU.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Passphrase:\"; ObjectID = \"NcS-0N-6uU\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عبارة المرور:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přístupová fráze:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Passphrase:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Frase de contraseña:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ביטוי סיסמה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासफ्रेज:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Frase d'accesso:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスフレーズ:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "암호:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wachtwoordzin:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Frase secreta:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Senha:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Парольная фраза:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Parola:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Парольна фраза:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "密码:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "通關密語:" } } } }, "Ncv-c4-Dmg.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Tab\"; ObjectID = \"Ncv-c4-Dmg\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Tab" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Tabulátor" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Tab" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Tab" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Tab" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Tab" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Tab" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टैब" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Tab" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タブ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Tab" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Tab" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tab" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tab" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Таб" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sekme" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Таб" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Tab" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "分頁" } } } }, "nCW-CZ-qC7.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Print\"; ObjectID = \"nCW-CZ-qC7\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "طباعة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Tisknout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Drucken" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Print" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Imprimir" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Imprimer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הדפס" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रिंट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Stampa" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "印刷" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "프린트" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Druk af" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Imprimir" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Imprimir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Печать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yazdır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Роздрукувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "打印" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "列印" } } } }, "Ng2-HH-bmY.label" : { "comment" : "Class = \"NSTabViewItem\"; label = \"Web\"; ObjectID = \"Ng2-HH-bmY\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Web" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "편물" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Веб" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Веб" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "生成网页" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "網頁" } } } }, "NHv-lG-vxt.title" : { "comment" : "Class = \"NSViewController\"; title = \"General\"; ObjectID = \"NHv-lG-vxt\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عام" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Obecné" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "General" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "General" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "General" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Général" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "כללי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सामान्य" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Generali" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "一般" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "일반" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Algemeen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Geral" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Geral" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Главные" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Genel" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Головні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "常规" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "一般" } } } }, "NMo-om-nkz.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Services\"; ObjectID = \"NMo-om-nkz\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الخدمات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Služby" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Services" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Services" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Servicios" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Services" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שירותים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सेवाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Servizi" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サービス" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "서비스" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Services" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Serviços" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Serviços" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сервисы" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Hizmetler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сервіси" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "服务" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "服務" } } } }, "nTf-oq-7k2.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Use TextBundle info.json to store c/mtime\"; ObjectID = \"nTf-oq-7k2\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "استخدام TextBundle Info.json لتخزين c/متى" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Použití TextBundle info.json pro uložení c/mtime" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle info.json zum Speichern von c/mtime verwenden" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Use TextBundle info.json to store c/mtime" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Utilizar TextBundle info.json para almacenar c/mtime" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Utiliser le TextBundle info.json pour stocker le c/mtime" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "השתמש בקובץ info.json של TextBundle כדי לאחסן c/mtime" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "c/mtime संग्रहीत करने के लिए TextBundle info.json का उपयोग करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Usare TextBundle info.json per memorizzare c/mtime" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "c/mtimeを格納するためにTextBundle info.jsonを使用する。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "텍스트 번들 info.json을 사용하여 c/mtime을 저장합니다." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Gebruik TextBundle info.json om c/mtime op te slaan" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Use TextBundle info.json para armazenar c/mtime" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Utilizar TextBundle info.json para armazenar c/mtime" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Использовать TextBundle info.json для хранения c/mtime" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Hizmetler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Використовувати TextBundle info.json для зберігання c/mtime" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "使用 TextBundle info.json 存储 c/mtime" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "使用 TextBundle info.json 儲存建立/修改時間" } } } }, "Nw2-km-iDW.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Touch ID:\"; ObjectID = \"Nw2-km-iDW\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "بصمة الاصبع:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Touch ID:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टच आईडी:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Vingerafdruk ID:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dokunmatik Kimlik:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Touch ID:" } } } }, "NZK-ki-p6Z.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Zoom In\"; ObjectID = \"NZK-ki-p6Z\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تكبير" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zvětšit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einzoomen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Zoom In" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Acercar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Agrandir" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "לְהִתְמַקֵד" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "ज़ूम इन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ingrandisci" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "拡大表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "줌인" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Inzoomen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Aumentar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ampliar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Увеличить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yakınlaştır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Збільшити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "放大" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "放大" } } } }, "o2f-1e-Dvp.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Reset\"; ObjectID = \"o2f-1e-Dvp\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Reset" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Resetovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Reset" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Reset" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Reset" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Reset" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Reset" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "रीसेट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Reset" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Reset" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Reset" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Reset" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Resetar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Reset" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сбросить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sıfırla" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Reset" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Reset" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重設" } } } }, "o6a-px-wp7.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Show in Terminal\"; ObjectID = \"o6a-px-wp7\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اظهار فيTerminal" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit v Terminálu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Zeigen in Terminal" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Show in Terminal" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar en la terminal" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher dans le Terminal" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג במסוף" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टर्मिनल में दिखाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra nel Terminale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ターミナルで開く" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "터미널에서 보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon in Terminal" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar no Terminal" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar no Terminal" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать в терминале" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Terminalde göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати в терміналі" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在终端中显示" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在終端機中顯示" } } } }, "O8Z-cg-XnH.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Import\"; ObjectID = \"O8Z-cg-XnH\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "استيراد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Importovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Importieren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Import" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Importar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Importer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ייבא" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "आयात" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Importa" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "取り込む..." } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "가져오기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Importeren" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Importar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Importar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Импортировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İçe aktar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Імпортувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "导入" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "匯入" } } } }, "O9f-Lf-HF6.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Show in Sidebar\"; ObjectID = \"O9f-Lf-HF6\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إظهار في الشريط الجانبي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit v bočním panelu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In der Seitenleiste anzeigen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Show in Sidebar" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar en la barra lateral" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher dans la barre latérale" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג בסרגל צד" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "साइडबार में दिखाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra nella barra laterale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サイドバーに表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "사이드 바에 표시" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon in zijbalk" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar no menu lateral" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar na barra lateral" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показывать в сайдбаре" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kenar Çubuğunda Göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показувати на бічній панелі" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在侧边栏中显示" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示於側邊欄" } } } }, "O9K-a1-3eu.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Title\"; ObjectID = \"O9K-a1-3eu\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "العنوان" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nadpis" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Title" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Title" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Título" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Titre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "כותרת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タイトル" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "제목" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Titel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Título" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlık" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "標題" } } } }, "oeL-rE-fXz.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"5\"; ObjectID = \"oeL-rE-fXz\"; Note = \"#bc-ignore!\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "5" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "5" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "5" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "5" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "5" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "5" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "5" } } } }, "oer-ZB-VOx.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"None (Use global settings)\"; ObjectID = \"oer-ZB-VOx\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "لا شيء (استخدام الإعدادات العامة)" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Žádné (použít globální nastavení)" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Globale einstellungen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "None (Use global settings)" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ajustes globales" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aucun (Utiliser les param. globaux)" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ללא (השתמש בהגדרות גלובליות)" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोई नहीं (वैश्विक सेटिंग का उपयोग करें)" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nessuno (Usa impostazioni globali)" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "自動" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "지정 안함 (기본 설정 사용)" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Geen (Gebruik globale instellingen)" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nenhum (use configuração padrão)" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nenhum (Utilizar definições globais)" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Не использовать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Hiçbiri (Genel ayarları kullan)" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Глобальні налаштування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无(使用全局设置)" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無 (使用全域設定)" } } } }, "oL6-fG-vHv.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Create Web Page\"; ObjectID = \"oL6-fG-vHv\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إنشاء صفحة ويب" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vytvořit webovou stránku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Web-Seite erstellen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Create Web Page" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Crear página web" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Créer une page Web" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "צור דף אינטרנט" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब पेज बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Crea pagina web" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ウェブページの作成" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "웹 페이지 만들기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Webpagina maken" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Criar página Web" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Criar página da Web" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать веб-страницу" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web Sayfası Oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Створити веб-сторінку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "创建为网页URL" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "建立網頁" } } } }, "Olw-nP-bQN.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Hide FSNotes\"; ObjectID = \"Olw-nP-bQN\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اخفاء FSNotes" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Skrýt FSNotes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes ausblenden" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Hide FSNotes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar FSNotes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Masquer FSNotes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסתר את FSNotes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes छिपाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nascondi FSNotes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FSNotesを非表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes 숨기기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verbergen FSNotes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar o FSNotes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar FSNotes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Спрятать FSNotes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes'u gizle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сховати FSNotes" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "隐藏 FSNotes" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "隱藏 FSNotes" } } } }, "OOL-dM-zIE.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Code Block\"; ObjectID = \"OOL-dM-zIE\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الاكواد البرمجية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Blok kódu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Blocksatz" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Code Block" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Bloque de código" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Bloc de code" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "בלוק קוד" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोड ब्लॉक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Blocco codice" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "コードブロック" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "코드 영역" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Codeblok" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Bloco de código" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Bloco de código" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Блок кода" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kod Bloğu" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Блок коду" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "代码块" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "程式碼區塊" } } } }, "oQE-3t-6pa.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Forward\"; ObjectID = \"oQE-3t-6pa\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إلى الأمام" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vpřed" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Vorwärts" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Forward" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Adelante" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Navigation vers l'avant" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "קדימה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "आगे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Avanti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "進む" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "앞으로" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Vooruit" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Avançar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Avançar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вперёд" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İleri" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вперед" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "向前" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "前進" } } } }, "OV0-om-POJ.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Print\"; ObjectID = \"OV0-om-POJ\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "طباعة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Tisknout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Drucken" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Print" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Imprimir…" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Imprimer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הדפס" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रिंट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Stampa" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "印刷" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "프린트" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Druk af" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Imprimir" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Imprimir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Печать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yazdır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Роздрукувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "打印" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "列印" } } } }, "OwM-mh-QMV.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Find Previous\"; ObjectID = \"OwM-mh-QMV\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "البحث السابق" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat předchozí" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Weitersuchen (rückwärts)" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Find Previous" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Buscar anterior" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Précédente occurrence" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מצא את הקודם" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पिछला खोजे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Trova precedente" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "前を探す" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이전 찾기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zoek Vorige" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Procurar anteriores" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar Anterior" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Найти предыдущий" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Öncekini Bul" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Знайти попередній" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "查找上一个" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "尋找上一個" } } } }, "Oyz-dy-DGm.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Stop Speaking\"; ObjectID = \"Oyz-dy-DGm\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ايقاف التحدث" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zastavit předčítání" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sprachausgabe stoppen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Stop Speaking" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Detener locución" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Arrêter la diction" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הפסק הקראה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बोलना बंद करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ferma Lettura" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "読み上げを停止" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "말하기 중단" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Stop met Praten" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Parar de falar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Parar Ditado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Остановить диктовку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Konuşmayı Bitir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зупинити диктування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "停止朗读" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "停止朗讀" } } } }, "P2a-yk-5Rx.title" : { "comment" : "Class = \"NSViewController\"; title = \"Layout\"; ObjectID = \"P2a-yk-5Rx\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نسق" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Rozložení" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Layout" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Layout" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Disposición" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Agencement" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מערך" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लेआउट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Layout" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "レイアウト" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "레이아웃" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Indeling" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Layout" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Layout" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Интерфейс" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Düzen" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Інтерфейс" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "布局" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "版面配置" } } } }, "P4M-cL-lfo.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Indent Using:\"; ObjectID = \"P4M-cL-lfo\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مسافة بادئة باستخدام:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odsazovat pomocí:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einzug mit:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Indent Using:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Sangría usando:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Instantanés automatiques:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שימוש בהזחה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इंडेंट के लिए उपयोग करें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rientro utilizzando:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インデント:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "자동 스냅샷:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Automatische snapshots:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Indentar usando:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Recuar usando:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Отступы:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Giriş Kullanarak:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відступ за допомогою:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "使用缩进:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "縮排方式:" } } } }, "P31-Ka-9HV.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Automatic Snapshots:\"; ObjectID = \"P31-Ka-9HV\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "لقطات تلقائية:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Automatické snímky:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Automatisches Backup:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Automatic Snapshots:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copia de seguridad automática:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Clichés instantanés automatiques :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תמונות מצב אוטומטיות:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्वचालित स्नैपशॉट:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Snapshot automatici:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "自動スナップショット:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "자동 스냅샷:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Automatische snapshots:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Snapshots automáticos:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Snapshots automáticos:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Стратегия резервного копирования:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Otomatik Anlık Görüntüler:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Автоматичні резервні копії:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "自动快照:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "自動快照:" } } } }, "pa3-QI-u2k.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Delete\"; ObjectID = \"pa3-QI-u2k\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حذف" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Smazat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Löschen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Delete" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מחק" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मिटाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Elimina" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "삭제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verwijder" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Deletar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sil" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "刪除" } } } }, "PdU-Em-Ugm.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Toggle Preview\"; ObjectID = \"PdU-Em-Ugm\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تبديل معاينة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přepnout náhled" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Vorschau umschalten" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Toggle Preview" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cambiar previsualización" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher l'aperçu" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג/הסתר תצוגה מקדימה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पूर्वावलोकन टॉगल करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Passa all'anteprima" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "プレビューの切り替え" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "미리보기 토글" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wissel voorvertoning" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Alterar pré-visualização" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Alternar pré-visualização" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переключить предпросмотр" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Önizlemeyi Aç/Kapat" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Переключити прев'ю" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "切换预览" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "切換預覽" } } } }, "PJ2-3m-IGm.placeholderString" : { "comment" : "Class = \"NSTextFieldCell\"; placeholderString = \"git@github.com:glushchenko/example.git\"; ObjectID = \"PJ2-3m-IGm\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/priklad.git" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "git@github.com:glushchenko/example.git" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/ornek.git" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "git@github.com:glushchenko/example.git" } } } }, "PLH-LL-geH.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Repeat Password:\"; ObjectID = \"PLH-LL-geH\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اعد كتابة كلمة المرور:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zopakujte heslo:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passwort wiederholen:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Repeat Password:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Repetir contraseña:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Retaper le mot de passe :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אישור סיסמה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासवर्ड दोहराएं:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ripeti password:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスワードを再入力:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "확인:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Herhaal wachtwoord:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Repita senha:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Repetir palavra-passe" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Повторите пароль:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Şifreyi tekrar girin:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Пароль ще раз:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重复密码:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "再次輸入密碼:" } } } }, "pnm-dP-GKO.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Duplicate\"; ObjectID = \"pnm-dP-GKO\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نسخة مكررة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Duplikovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Duplizieren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Duplicate" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Duplicar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dupliquer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שכפל" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नकल" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Duplica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "複製" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "복제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Dupliceer" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Duplicar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Duplicar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать копию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Дублювати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "生成副本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "製作複本" } } } }, "PXw-YJ-q6A.title" : { "comment" : "Class = \"NSViewController\"; title = \"Security\"; ObjectID = \"PXw-YJ-q6A\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الحماية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zabezpečení" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sicherheit" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Security" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Seguridad" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sécurité" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אבטחה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सुरक्षा" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sicurezza" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "セキュリティ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "보안" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Veiligheid" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Segurança" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Segurança" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Безопасность" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Güvenlik" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Безпека" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "安全性" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "安全性" } } } }, "PY8-lr-5qr.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Creation date\"; ObjectID = \"PY8-lr-5qr\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تاريخ الانشاء" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Datum vytvoření" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erstellungsdatum" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Creation date" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Fecha de creación" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Date de création" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תאריך יצירה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "निर्माण तारीख" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Data di creazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "作成日" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "생성일" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Aanmaakdatum" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Data de criação" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Data de criação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Дата создания" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Oluşturulma tarihi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Датою створення" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "创建日期" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "建立日期" } } } }, "Pzb-TG-43d.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Headers\"; ObjectID = \"Pzb-TG-43d\";", "extractionState" : "extracted_with_value", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", "value" : "Kopfzeilen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Headers" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Encabezados" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "En-têtes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Intestazioni" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заголовки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlıklar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заголовки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "標題" } } } }, "PzH-8q-38O.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Delete Web Page\"; ObjectID = \"PzH-8q-38O\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حذف صفحة الويب" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Smazat webovou stránku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Geteilte aktualisieren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Delete Web Page" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar página web" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer la page Web" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מחק דף אינטרנט" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब पेज हटाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Elimina pagina web" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ウェブページを削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "웹 페이지 삭제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Webpagina verwijderen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Deletar página Web" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Excluir página da Web" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить веб-страницу" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web Sayfasını Sil" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити веб-сторінку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除网页" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "刪除網頁" } } } }, "q09-fT-Sye.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Find Next\"; ObjectID = \"q09-fT-Sye\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "بحث عن التالي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat další" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Weitersuchen (vorwärts)" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Find Next" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Buscar siguiente" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Prochaine occurrence" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מצא את הבא" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अगला खोजे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Trova successivo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "次を探す" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다음 찾기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zoek Volgende" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Procurar próximo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar Próximo" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Найти следующий" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sonrakini Bul" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Знайти далі" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "查找下一个" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "尋找下一個" } } } }, "Q9n-yE-Ul0.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Report Bugs or Feature Requests\"; ObjectID = \"Q9n-yE-Ul0\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تبليغ عن خطأ برمجي او طلب ميزة جديدة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Ohlásit chyby nebo návrhy pro zlěpšení" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Report Bugs or Feature Requests" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Report Bugs or Feature Requests" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Informar de Errores o Mejoras" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Remonter des bugs ou des demandes de fonctionnalités" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "דוחות באגים ובקשות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "समस्या या सुविधा अनुरोध की रिपोर्ट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Segnala bug o richiedi una funzionalità" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "バグや機能要望の報告" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "버그 보고 및 기능 제안" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Meld bugs of functieverzoeken" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Reportar erros ou solicitação de funcionalidades" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Reportar Bugs ou Pedir Funcionalidades" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сообщить об ошибках" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Hataları veya Özellik İsteklerini Bildirin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Повідомити про помилки або запитати про нові функції" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "报告错误或者提交功能请求" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "回報錯誤或功能請求" } } } }, "QdU-og-lQH.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Change Creation Date\"; ObjectID = \"QdU-og-lQH\"; Note = \"#bc-ignore!\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تغيير تاريخ الإنشاء" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Změnit datum vytvoření" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erstellungsdatum ändern" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Change Creation Date" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cambiar fecha de creación" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Modifier la date de création" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה את תאריך היצירה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "निर्माण तारीख बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Modifica data di creazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "作成日の変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "생성 날짜 변경" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wijzig de aanmaakdatum" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Alterar data de criação" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Alterar data de criação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Изменить дату создания" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Oluşturma Tarihini Değiştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Змінити дату створення" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更改创建日期" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "變更建立日期" } } } }, "Qkz-nR-Fh5.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Git repository:\"; ObjectID = \"Qkz-nR-Fh5\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "أو استخدم المفتاح الخاص .ssh (موصى به):" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Git repozitář:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Oder verwenden Sie einen privaten .ssh-Schlüssel (empfohlen):" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Git repository:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "O use la clave privada .ssh (recomendado):" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ou utilisez la clé privée .ssh (recommandé):" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "או השתמש במפתח פרטי .ssh (מומלץ):" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गिट रिपोजिटरी" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Oppure usa la chiave privata .ssh (consigliato):" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "または、.ssh 秘密鍵を使用します (推奨):" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "또는 .ssh 개인 키 사용(권장):" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Of gebruik de .ssh-privésleutel (aanbevolen):" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Repositório Git:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ou use a chave privada .ssh (recomendado):" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Git репозиторий:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Git deposu:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Або використовуйте .ssh приватний ключ (рекомендується):" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "或使用 .ssh 私钥(推荐):" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Git 儲存庫:" } } } }, "qn1-vy-8oB.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Format: yyyy-MM-dd hh.mm.ss a\"; ObjectID = \"qn1-vy-8oB\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Format: yyyy-MM-dd hh.mm.ss a" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Formát: yyyy-MM-dd hh.mm.ss a" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Format: yyyy-MM-dd hh.mm.ss a" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Format: yyyy-MM-dd hh.mm.ss a" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Formato: yyyy-MM-dd hh.mm.ss a" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Format: yyyy-MM-dd hh.mm.ss a" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פורמט: yyyy-MM-dd hh.mm.ss a" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रारूप: yyyy-MM-dd hh.mm.ss a" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Format: yyyy-MM-dd hh.mm.ss a" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォーマット: yyyy-MM-dd hh.mm.ss a" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "체재: yyyy-MM-dd hh.mm.ss a" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Format: yyyy-MM-dd hh.mm.ss a" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Formato: yyyy-MM-dd hh.mm.ss a" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Formato: yyyy-MM-dd hh.mm.ss a" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Формат: yyyy-MM-dd hh.mm.ss a" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Biçim: yyyy-MM-dd hh.mm.ss a" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Формат: yyyy-MM-dd hh.mm.ss a" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "格式: yyyy-MM-dd hh.mm.ss a" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "格式:yyyy-MM-dd hh.mm.ss a" } } } }, "qNE-oZ-zJr.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Back\"; ObjectID = \"qNE-oZ-zJr\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عودة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zpět" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Zurück" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Back" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Atrás" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Navigation vers l'arrière" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אחורה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पीछे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Indietro" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "戻る" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "뒤" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Terug" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Voltar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Voltar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Назад" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Geri" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Назад" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "返回" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "返回" } } } }, "qs2-5J-Q3n.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Show Options\"; ObjectID = \"qs2-5J-Q3n\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عرض الاعدادات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit možnosti" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einstellungen anzeigen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Show Options" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ver configuración" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher les options d'affichage" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג אפשרויות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "विकल्प दिखाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra opzioni vista" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "オプションを表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "보기 옵션" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon weergaveopties" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar as opções" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar opções de visualização" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Настроить вид" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seçenekleri Göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Налаштування вигляду" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "显示视图选项" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示選項" } } } }, "QxK-39-9yF.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Direction:\"; ObjectID = \"QxK-39-9yF\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اتجاه:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Směr:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Richtung:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Direction:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Dirección:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Direction :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "סדר:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "दिशा:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Direzione:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "方向:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "방향:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Richting:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Direção:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Direcção:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Направление:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yön:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Напрямок:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "方向:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "方向:" } } } }, "qZf-Xp-kDW.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Main Window\"; ObjectID = \"qZf-Xp-kDW\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "النافذة الرئيسية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hlavní okno" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Hauptfenster" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Main Window" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ventana principal" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Fenêtre principale" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חלון ראשי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मुख्य विंडो" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Finestra principale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "メインウィンドウ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "주 윈도우" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hoofd Venster" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Janela principal" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Janela Principal" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Главное окно" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ana Pencere" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Головне Вікно" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "主窗口" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "主視窗" } } } }, "r2R-xe-eCA.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Focus in editor when selecting note\"; ObjectID = \"r2R-xe-eCA\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "التركيز في المحرر عند اختيار المذكرة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Při výběru poznámky přepnout do editoru" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Fokus im Editor bei Auswahl der Notiz" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Focus in editor when selecting note" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Empezar con la edición al seleccionar una nota" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Curseur dans l'éditeur lorsqu'une note est sélectionnée" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "התמקד בעורך בבחירת פתק" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट चुनते समय संपादक में फ़ोकस करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Evidenzia nell'editor quando si seleziona la nota" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ノート選択時にエディタへ移動する" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "노트 선택 시 편집기 활성화" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Focus in editor bij selecteren notitie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Foco no editor ao selecionar a nota" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Focar no editor quando seleccionar nota" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Фокус в редактор при выборе заметки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Not seçerken düzenleyicide odaklanın" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Фокус в редактор при виборі нотатки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "当选中笔记时编辑器自动聚焦(笔记内容处理编辑状态)" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "選取筆記時將焦點移至編輯器" } } } }, "r69-B6-vT3.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Margin Size:\"; ObjectID = \"r69-B6-vT3\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حجم الهامش:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Velikost okrajů:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Absatzgröße: " } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Margin Size:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Tamaño del margen: " } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Taille de marge: " } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גודל שוליים:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मार्जिन आकार:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Dimensione margine: " } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "マージン幅:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "여백 크기:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Marge grootte: " } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tamanho da margem:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tamanho da margem:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Отступ абзаца:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Marj Boyutu:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відступ абзацу:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "边距大小: " } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "邊界大小:" } } } }, "rbD-Rh-wIN.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Check Spelling While Typing\"; ObjectID = \"rbD-Rh-wIN\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تحقق من التدقيق الإملائي أثناء الكتابة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kontrolovat pravopis během psaní" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Während der Texteingabe prüfen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Check Spelling While Typing" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Comprobar ortografía mientras se escribe" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Vérifier l'orthographe pendant l'écriture" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "בדוק איות תוך כדי הקלדה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टाइप करते समय वर्तनी जाँचें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Controlla l'ortografia durante la digitazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "入力中にスペルをチェック" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "입력하는 동안 맞춤법 검사" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Controleer Spelling Tijdens Typen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Verificar ortografia enquanto digita" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Verificar gramática durante a escrita" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Проверять во время набора" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yazarken Yazım Denetimi Yapın" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перевіряти правопис під час набору" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "键入时检查拼写" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "輸入時同步檢查拼字" } } } }, "rbM-i0-QFp.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Reset\"; ObjectID = \"rbM-i0-QFp\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إعادة ضبط" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Resetovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Zurücksetzen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Reset" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Reiniciar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Réinitialiser" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אִתחוּל" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "रीसेट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ripristina" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リセット" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "초기화" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Resetten" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Resetar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Redefinir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сбросить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sıfırla" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скинути" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重設" } } } }, "RdI-N4-pR2.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Trash\"; ObjectID = \"RdI-N4-pR2\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الملهملات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Koš" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Papierkorb" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Trash" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Papelera" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Corbeille" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פח אשפה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कूडा" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cestino" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ゴミ箱" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "휴지통" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Prullenmand" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Lico" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Lixo" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Корзина" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Çöp" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сміття" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "回收站" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "垃圾桶" } } } }, "Rg2-j1-Foz.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Copy URL\"; ObjectID = \"Rg2-j1-Foz\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نسخ URL" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kopírovat URL" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "URL kopieren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Copy URL" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copiar enlace" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copier l'URL" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העתק כתובת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "यूआरएल कॉपी करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copia URL" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "URLをコピー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "URL 복사" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kopieer URL" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Copiar URL" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Copiar endereço URL" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Скопировать ссылку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "URLyi Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скопіювати посилання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "拷贝 URL" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "複製網址" } } } }, "rgM-f4-ycn.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Smart Dashes\"; ObjectID = \"rgM-f4-ycn\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "شرطات ذكية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Inteligentní pomlčky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Intelligente Bindestriche" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Smart Dashes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Guiones inteligentes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Tirets intelligents" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מיקוף חכם" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्मार्ट डैश" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Trattini Smart" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "スマートダッシュ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "스마트 대시" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Slimme Streepjes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Traços inteligentes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Traços Inteligentes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Смарт-тире" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Akıllı çizgi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розумні тире" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "智能破折号" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "智慧型破折號" } } } }, "rHB-xF-GgG.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Sort By\"; ObjectID = \"rHB-xF-GgG\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ترتيب حسب" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Řadit podle" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sortiere nach" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Sort By" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ordenar por" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Trier par" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מיין לפי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इसके अनुसार क्रमबद्ध करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ordina per" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "整頓順序" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다음으로 정렬" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Sorteer op" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ordernar por" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ordenar por" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Упорядочить по" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Göre sırala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сортувати за" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "排序方式" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "排序方式" } } } }, "Ri7-Yr-qyB.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Clone\"; ObjectID = \"Ri7-Yr-qyB\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Klonovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Clone" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्लोन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Clonar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Клонировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klon" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Clone" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "克隆" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "複製專案" } } } }, "RSs-wb-GNg.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Lock/Unlock\"; ObjectID = \"RSs-wb-GNg\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فقل/فتح الفقل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zamknout/odemknout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sperren/Entsperren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Lock/Unlock" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear/desbloquear" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Verrouiller/déverrouiller" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נעל/פתח" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लॉक/अनलॉक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Blocca/sblocca" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ロック/ロックの削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "잠금/잠금 해제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Vergrendel/ontgrendel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear/desbloquear" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear/desbloquear" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заблокировать/разблокировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kilitle/Kilidi Aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заблокувати/розблокувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "锁定/解锁" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "鎖定/解鎖" } } } }, "rSZ-Wo-Rtj.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"System\"; ObjectID = \"rSZ-Wo-Rtj\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "النظام" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Systém" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "System" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "System" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Sistema" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Système" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מערכת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सिस्टम" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sistema" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "システム" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "시스템" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Systeem" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Sistema" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Sistema" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Система" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sistem" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Системна" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "系统" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "系統" } } } }, "Ruw-6m-B2m.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Select All\"; ObjectID = \"Ruw-6m-B2m\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تحديد الكل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vybrat vše" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Alles auswählen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Select All" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Seleccionar todo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Tout sélectionner" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "בחר הכל" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सबका चयन करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Seleziona Tutto" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "すべてを選択" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "모두 선택" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Selecteer Alles" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Selecionar todos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Seleccionar todos" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Выбрать всё" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Hepsini Seç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вибрати все" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "选择全部" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "全選" } } } }, "RwR-P0-ycK.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Delete Folder\"; ObjectID = \"RwR-P0-ycK\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حذف المجلد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Smazat složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner löschen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Delete Folder" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer le dossier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מחק תיקיה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फोल्डर हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Elimina cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォルダを削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "폴더 삭제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verwijder map" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Deletar pasta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить директорию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dosyayı Sİl" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити директорію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除文件夹" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "刪除資料夾" } } } }, "rXU-X3-UjC.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Reveal in Finder\"; ObjectID = \"rXU-X3-UjC\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فتح المجلد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit ve Finderu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In Finder anzeigen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Reveal in Finder" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar en el Finder" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Localiser dans le Finder" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג ב-Finder" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Finder में दिखाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra nel Finder" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Finderに表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Finder에서 보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Onthul in Finder" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Revelar no Finder" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Realçar no Finder" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать в Finder" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Finder'da ortaya çıkar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати в Finder" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在访达中显示" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示於 Finder" } } } }, "rzB-ln-uyl.placeholderString" : { "comment" : "Class = \"NSSecureTextFieldCell\"; placeholderString = \"passphrase\"; ObjectID = \"rzB-ln-uyl\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "přístupová fráze" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "passphrase" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासफ्रेज" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "frase secreta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Parola" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "通關密語" } } } }, "S0p-oC-mLd.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Jump to Selection\"; ObjectID = \"S0p-oC-mLd\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "الانتقال الى المحدد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přeskočit na výběr" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Zur Auswahl" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Jump to Selection" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ir a la selección" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sauter vers la sélection" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "קפוץ לבחירה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "चयन पर जाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Vai alla selezione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Jump to Selection" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "선택 부분으로 이동" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Spring naar Selectie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pular para seleção" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Saltar para a selecção" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Перейти к выбранному" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seçime Git" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейти до вибраного" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "跳到所选内容" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "跳至選取範圍" } } } }, "s6A-tQ-Sbe.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Bring All to Front\"; ObjectID = \"s6A-tQ-Sbe\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "أحضر الكل إلى الأمام" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Převést vše do popředí" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Alle nach vorne bringen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Bring All to Front" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Traer todo al frente" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Tout afficher en premier plan" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הבא הכל קדימה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सबको सामने लाये" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Porta tutto in primo piano" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "すべてを手前に移動" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "모두 앞으로 가져오기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Breng alles naar voren" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Trazer todos para frente" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Trazer todos para a frente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переместить на передний план" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Hepsini Öne Getir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перемістити все на передній план" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "前置全部窗口" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "將此程式所有視窗移至最前" } } } }, "S8Z-Js-fwB.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Add External Folder...\"; ObjectID = \"S8Z-Js-fwB\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ارفاق تخزين..." } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přidat externí složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Speicher anschließen..." } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Add External Folder..." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Adjuntar almacenamiento..." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Attacher un stockage..." } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הוסף תיקיה חיצונית..." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बाह्य फ़ोल्डर जोड़ें..." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Collega archivio..." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "外部フォルダを追加…" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "저장소 추가..." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Koppelen opslag..." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Adicionar pasta externa..." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Adicionar armazenamento..." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Подключить хранилище..." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Harici Klasör Ekle..." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Підключити сховище..." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "附件存储..." } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "加入外部資料夾…" } } } }, "sAn-Wm-FfY.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Commit/Push every\"; ObjectID = \"sAn-Wm-FfY\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "النسخ الاحتياطي كل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Commit/push každých" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sichern Sie jeden" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Commit/Push every" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Respaldar cada" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sauvegarder toutes les " } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גבה אוטומטית כל" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कमिट/पुश आवृत्ति" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Salva ogni" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "次の間隔でバックアップ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "백업" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Back-up elke" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Commi/Push todos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Cópia de seg. a cada" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Копия каждый" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Her Taahhüt Et/İt" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Резервна копія кожну" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "提交或推送间隔每" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "自動提交/推送頻率" } } } }, "Scc-aD-CFQ.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Notes List:\"; ObjectID = \"Scc-aD-CFQ\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "قائمة الملاحظات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Seznam poznámek:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notizlist:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Notes List:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Lista de notas:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Liste de notes :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "רשימת פתקים:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट सूची:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Lista Note:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ノート一覧:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "노트 목록:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Notitielijst:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Lista de notas:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Lista de notas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Список заметок:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Not Listesi:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Лист нотаток:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "笔记列表:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "筆記清單:" } } } }, "sdE-ZM-6cq.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Move Up in the Sidebar\"; ObjectID = \"sdE-ZM-6cq\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تحرك لأعلى في الشريط الجانبي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Posunout nahoru v bočním panelu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In der Seitenleiste aufsteigen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Move Up in the Sidebar" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mover arriba en la barra lateral" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Élément précédent de la barre latérale" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עבור למעלה בסרגל הצד" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "साइडबार में ऊपर जाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Muovi in Alto nella Sidebar" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サイドバー項目を上に移動" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "사이드바에서 위로 이동" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Omhoog gaan in de zijbalk" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mover para cima no menu lateral" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Subir na barra lateral" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Перейти вверх в боковой панели" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kenar Çubuğunda Yukarı Çık" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейти вгору на бічній панелі" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在侧边栏中向上移动" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在側邊欄中上移" } } } }, "SdL-vK-KjH.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Window\"; ObjectID = \"SdL-vK-KjH\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نافذة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Okno" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Fenster" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Window" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ventana" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Fenêtre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חלון" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "विंडो" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Finestra" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ウィンドウ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "윈도우" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Venster" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Janela" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Janela" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Window" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Pencere" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вікно" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "窗口" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "視窗" } } } }, "SDz-2X-4yJ.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Reset Caches\"; ObjectID = \"SDz-2X-4yJ\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إعادة تعيين ذاكرة التخزين المؤقت" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vyprázdnit cache" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Caches zurücksetzen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Reset Caches" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Restablecer cachés" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Réinitialiser les caches" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אפס מטמונים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कैश रीसेट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ripristino delle cache" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "キャッシュのリセット" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "캐시 초기화" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Caches resetten" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Redefinir caches" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Repor caches" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сбросить кэш" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Önbellekleri Sıfırla" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скинути кеші" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重置缓存" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重設快取" } } } }, "SeY-r8-n3w.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"File\"; ObjectID = \"SeY-r8-n3w\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ملف" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Soubor" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Datei" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "File" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Archivo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Fichier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "קובץ" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ाइल" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "File" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ファイル" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "파일" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Bestand" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Arquivo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ficheiro" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Файл" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dosya" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Файл" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "文件" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "檔案" } } } }, "Shc-VO-eTc.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Code Font\"; ObjectID = \"Shc-VO-eTc\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "كود الخط" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Písmo kódu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Code Font" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Code Font" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Code Font" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Police de code" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גופן קוד" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोड फ़ॉन्ट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Font Codice" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "コードフォント" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "코드 서체" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Code Lettertype" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Código fonte" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tipo de letra de código" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Шрифт кода" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kod Yazı Tipi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Шрифт коду" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "代码字体" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "程式碼字體" } } } }, "Snd-dJ-uj5.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"History\"; ObjectID = \"Snd-dJ-uj5\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تاريخ" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Historie" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Die geschichte" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "History" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Historia" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Historique" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "היסטוריה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इतिहास" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cronologia" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "履歴" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "변경 이력" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Geschiedenis" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Histórico" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Histórico" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "История" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tarih" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Історія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "笔记历史版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "歷程記錄" } } } }, "SpX-N8-9td.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"solarized\"; ObjectID = \"SpX-N8-9td\";", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "solarized" } }, "ja" : { "stringUnit" : { "state" : "needs_review", "value" : "Solarized Light" } }, "pt-PT" : { "stringUnit" : { "state" : "needs_review", "value" : "Salaried-light" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "solarized" } } }, "shouldTranslate" : false }, "SrH-fe-Hog.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"New Folder\"; ObjectID = \"SrH-fe-Hog\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "needs_review", "value" : "مجلد جديد" } }, "cs" : { "stringUnit" : { "state" : "needs_review", "value" : "Vytvořit složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neuer Ordner" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "New Folder" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nueva carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nouveau dossier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תיקיה חדשה" } }, "hi" : { "stringUnit" : { "state" : "needs_review", "value" : "फोल्डर बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuova cartella" } }, "ja" : { "stringUnit" : { "state" : "needs_review", "value" : "フォルダを作成" } }, "ko" : { "stringUnit" : { "state" : "needs_review", "value" : "새로운 폴더" } }, "nl-NL" : { "stringUnit" : { "state" : "needs_review", "value" : "Nieuwe map" } }, "pt-BR" : { "stringUnit" : { "state" : "needs_review", "value" : "Criar pasta" } }, "pt-PT" : { "stringUnit" : { "state" : "needs_review", "value" : "Nova pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать директорию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasör Oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нова директорія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "新建文件夹" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "新增資料夾" } } } }, "ssC-ed-19K.title" : { "comment" : "Class = \"NSViewController\"; title = \"Editor\"; ObjectID = \"ssC-ed-19K\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "المحرر" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Editor" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Editor" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Editor" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Editor" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Éditeur" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עורך" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संपादक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Editor" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "エディタ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "편집기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Editor" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Editor" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Editor" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Редактор" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Editör" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Редактор" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "编辑器" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "編輯器" } } } }, "SSY-US-gmJ.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Show icon in menu bar\"; ObjectID = \"SSY-US-gmJ\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "عرض الايقونة في شريط القوائم" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit ikonu v řádku nabídek" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Icon in der Menüleiste anzeigen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Show icon in menu bar" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar icono en la barra de menús" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher l'icône dans la barre de menu" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג צלמית בשורת הטפריטים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मेनू बार में आइकन दिखाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra icona nella barra del menù" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "メニューバーにアイコンを表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "메뉴바에서 아이콘 보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon icoon in menubalk" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar ícone na barra de menu" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar ícone na barra de menu" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показывать иконку в меню баре" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Menü çubuğunda simgeyi göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відображати іконку в меню барі" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在菜单栏中显示图标" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在選單列顯示圖示" } } } }, "STm-3f-GNu.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Move Selected Lines Down\"; ObjectID = \"STm-3f-GNu\";", "extractionState" : "extracted_with_value", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", "value" : "Ausgewählte Zeilen nach unten verschieben" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Move Selected Lines Down" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mover líneas seleccionadas hacia abajo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déplacer les lignes sélectionnées vers le bas" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "चुनी हुई पंक्तियों को नीचे ले जाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sposta righe selezionate verso il basso" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переместить выбранные строки вниз" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seçili Satırları Aşağı Taşı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перемістити вибрані рядки вниз" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "选中行向下移动" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "將選取的行下移" } } } }, "sWS-bo-99f.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Dylan Seeger — https://www.lovably.com\\nOlena Hlushcneko\"; ObjectID = \"sWS-bo-99f\"; Note = \"#bc-ignore!\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Dylan Seeger — https://www.lovably.com\nOlena Hlushcneko" } } } }, "t3f-wS-BqN.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Automatic iCloud Drive conflicts resolution\"; ObjectID = \"t3f-wS-BqN\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حل تعارضات iCloud Drive تلقائيًا" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Automatické řešení konfliktů na iCloud Drive" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Automatische Auflösung von iCloud Drive-Konflikten" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Automatic iCloud Drive conflicts resolution" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Resolver automáticamente conflictos de iCloud Drive" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Résolution automatique des conflits avec iCloud Drive" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פתור אוטומטית התנגשויות iCloud Drive" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्वचालित iCloud ड्राइव विवाद समाधान" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Risoluzione automatica dei conflitti di iCloud Drive" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Driveの競合を自動的に解決する" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive 충돌 자동 해결" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Automatisch iCloud Drive-conflicten oplossen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Resolva automaticamente conflitos do iCloud Drive" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Resolva automaticamente conflitos do iCloud Drive" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Автоматически разрешать конфликты iCloud Drive" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Otomatik iCloud Drive çakışma çözümü" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Автоматичне вирішення конфліктів у iCloud Drive" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "自动解决 iCloud Drive 冲突" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "自動解決 iCloud 雲碟衝突" } } } }, "TCI-Xs-mwm.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Descending\"; ObjectID = \"TCI-Xs-mwm\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تنازلي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Sestupně" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Absteigend" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Descending" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Descendiendo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Descendant" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "יורד" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अवरोही" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Decrescente" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "降順" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "내림차순" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Aflopend" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Descendente" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Descendente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Нисходящее" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Azalan" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Низхідний" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "降序" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "遞減" } } } }, "TfH-z4-HHY.title" : { "comment" : "Class = \"NSMenuItem\"; title = \".txt\"; ObjectID = \"TfH-z4-HHY\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "en" : { "stringUnit" : { "state" : "new", "value" : ".txt" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : ".txt" } } } }, "Tjy-Pz-wrj.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Appearance:\"; ObjectID = \"Tjy-Pz-wrj\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مظهر خارجي:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vzhled:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Design:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Appearance:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Apariencia:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Apparence :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מראה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्वरूप:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Aspetto:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "外観モード:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "외관:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verschijning:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Aparência:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Aparência:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Тема:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dış görünüş:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Тема:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "外观:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "外觀:" } } } }, "TKE-yB-wPs.ibShadowedToolTip" : { "comment" : "Class = \"NSImageView\"; ibShadowedToolTip = \"In memory of Mars. ? - 25.08.2022\"; ObjectID = \"TKE-yB-wPs\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "In memory of Mars. ? - 25.08.2022" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Věnování: Mars. ?–25.08.2022" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In memory of Mars. ? - 25.08.2022" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "In memory of Mars. ? - 25.08.2022" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "In memory of Mars. ? - 25.08.2022" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "In memory of Mars. ? - 25.08.2022" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "In memory of Mars. ? - 25.08.2022" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मंगल की याद में. ? - 25.08.2022" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "In memory of Mars. ? - 25.08.2022" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "In memory of Mars. ? - 25.08.2022" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "In memory of Mars. ? - 25.08.2022" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "In memory of Mars. ? - 25.08.2022" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Em memória de Mars. ? - 25.08.2022" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "In memory of Mars. ? - 25.08.2022" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "В память о Марсе. ? - 25.08.2022" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Mars'ın anısına. ? - 25.08.2022" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "In memory of Mars. ? - 25.08.2022" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "In memory of Mars. ? - 25.08.2022" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "紀念 Mars。? - 2022.08.25" } } } }, "tMJ-Dj-OZc.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"First line as title\"; ObjectID = \"tMJ-Dj-OZc\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اول سطر كعنوان" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "První řádek jako nadpis" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erste Zeile als Titel" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "First line as title" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Primera línea como título" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Première ligne comme titre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שורה ראשונה ככותרת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक के रूप में पहली पंक्ति" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Prima riga come titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "最初の行をタイトルとして使用" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "첫 줄을 제목으로 사용" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Eerste regel als titel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Primeira linha como título" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Primeira linha como título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Первая строка как заголовок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlık olarak ilk satır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перший рядок як заголовок" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "将内容第一行提取为标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "使用第一行作為標題" } } } }, "tR0-Uj-4tB.title" : { "comment" : "Class = \"NSTabViewController\"; title = \"Preferences\"; ObjectID = \"tR0-Uj-4tB\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "التفضيلات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Předvolby" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einstellungen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Preferences" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Preferencias" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Préférences" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העדפות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्राथमिकताएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Preferenze" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "設定" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "환경설정" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voorkeuren" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Preferências" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Preferências" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Настройки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tercihler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Налаштування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "偏好设置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "偏好設定" } } } }, "tRr-pd-1PS.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Data Detectors\"; ObjectID = \"tRr-pd-1PS\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "كاشفات البيانات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Detektory dat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Datendetektoren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Data Detectors" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Detectores de datos" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Détecteurs de données" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גלאי נתונים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डेटा डिटेक्टर" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rilevatori di dati" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "データ検出" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "데이터 탐색기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Data Detectoren" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Detecção de data" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Detectores de dados" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Детекторы данных" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Veri Dedektörleri" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Детектори даних" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "数据检测器" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "資料偵測器" } } } }, "tZ6-GT-LxK.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Pull every\"; ObjectID = \"tZ6-GT-LxK\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Pull every" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Pull každých" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Pull every" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Pull every" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Pull every" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Tirez chaque" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Pull every" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पुल आवृत्ति" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Pull every" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Pull every" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Pull every" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Pull every" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pull todos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pull every" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Подтягивать каждые" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Her birini çek" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Pull every" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "拉取分支每隔" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "每隔…拉取 " } } } }, "u88-5C-vTe.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Open\"; ObjectID = \"u88-5C-vTe\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "needs_review", "value" : "افتح في نافذة جديدة" } }, "cs" : { "stringUnit" : { "state" : "needs_review", "value" : "Otevřít v novém okně" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In neuem Fenster öffnen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Open" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Abrir en Nueva ventana" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir dans une nouvelle fenêtre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פתח בחלון חדש" } }, "hi" : { "stringUnit" : { "state" : "needs_review", "value" : "नई विंडो में खोलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apri in una nuova finestra" } }, "ja" : { "stringUnit" : { "state" : "needs_review", "value" : "新しいウィンドウで開きます" } }, "ko" : { "stringUnit" : { "state" : "needs_review", "value" : "새 창에서 열기" } }, "nl-NL" : { "stringUnit" : { "state" : "needs_review", "value" : "Openen in een nieuw venster" } }, "pt-BR" : { "stringUnit" : { "state" : "needs_review", "value" : "Abrir em nova janela" } }, "pt-PT" : { "stringUnit" : { "state" : "needs_review", "value" : "Abrir em nova janela" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открыть в новом окне" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni Pencerede Aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відкрити у новому вікні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在新窗口中打开" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "開啟" } } } }, "UdE-8N-Qit.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Label\"; ObjectID = \"UdE-8N-Qit\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Label" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Štítek" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Label" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Label" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Label" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Label" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Label" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लेबल" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Label" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Label" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Label" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Label" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Etiqueta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Label" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Label" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etiket" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ярлик" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "标签" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "標籤" } } } }, "UEZ-Bs-lqG.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Capitalize\"; ObjectID = \"UEZ-Bs-lqG\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تكبير" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Velká počáteční písmena" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Großbuchstaben" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Capitalize" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mayúsculas iniciales" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ajouter les majuscules aux mots" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אות ראשונה גדולה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मूल बनाए" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Tutte le iniziali Maiuscole" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "語頭を大文字にする" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "대문자로 만들기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Met hoofdletters schrijven" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Capitalizar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Capitalizar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Озаглавить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Büyük harfle yazmak" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Усі прописні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "首字母大写" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "首字大寫" } } } }, "UFh-mf-ZKG.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Light\"; ObjectID = \"UFh-mf-ZKG\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مشمس" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Světlý" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Hell" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Light" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Claro" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Clair" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "בהיר" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Light" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Chiaro" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ライト" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "라이트" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Licht" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Claro" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Claro" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Светлая" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Işık" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Світла" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "浅色" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "淺色" } } } }, "uHf-35-KHR.placeholderString" : { "comment" : "Class = \"NSTextFieldCell\"; placeholderString = \"22\"; ObjectID = \"uHf-35-KHR\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "22" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "22" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "22" } } } }, "Ukg-MO-eaB.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Code Span\"; ObjectID = \"Ukg-MO-eaB\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "امتداد الرمز" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Úsek kódu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Codespanne" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Code Span" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Intervalo de código" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Code Span" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "קוד מוטבע" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोड स्पैन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Intervallo di Codice" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インラインコード" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "코드 들여쓰기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Code inline" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Código de span" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Lapso de código" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Диапазон кода" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kod aralığı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Проміжок коду" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "代码缩进" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "程式碼區段" } } } }, "UMi-Hx-Z1c.title" : { "comment" : "Class = \"NSMenu\"; title = \"Headers\"; ObjectID = \"UMi-Hx-Z1c\";", "extractionState" : "extracted_with_value", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", "value" : "Kopfzeilen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Headers" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Encabezados" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "En-têtes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Intestazioni" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заголовки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlıklar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заголовки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "标题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "標題" } } } }, "uMZ-sI-7ja.title" : { "comment" : "Class = \"NSMenu\"; title = \"File\"; ObjectID = \"uMZ-sI-7ja\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ملف" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Soubor" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Datei" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "File" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Archivo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Fichier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "קובץ" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ाइल" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "File" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ファイル" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "파일" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Bestand" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Arquivo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ficheiro" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Файл" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dosya" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Файл" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "文件" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "檔案" } } } }, "uQ1-ej-ufE.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Copy URL\"; ObjectID = \"uQ1-ej-ufE\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نسخ URL" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kopírovat URL" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "URL kopieren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Copy URL" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copiar enlace" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copier l'URL" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העתק כתובת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "यूआरएल कॉपी करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copia URL" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "URLをコピー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "URL 복사" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kopieer URL" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Copiar URL" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Copiar endereço URL" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Скопировать ссылку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "URLyi Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скопіювати посилання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "拷贝URL" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "複製網址" } } } }, "uQy-DD-JDr.title" : { "comment" : "Class = \"NSMenu\"; title = \"FSNotes\"; ObjectID = \"uQy-DD-JDr\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "FSNotes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } } } }, "uRl-iY-unG.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Cut\"; ObjectID = \"uRl-iY-unG\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "قص" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vyjmout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ausschneiden" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Cut" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cortar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Couper" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גזור" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "काटे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Taglia" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "カット" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "오려두기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Knip" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Cortar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Cortar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вырезать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kes" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вирізати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "剪切" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "剪下" } } } }, "v1K-9W-2aB.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"github\"; ObjectID = \"v1K-9W-2aB\";", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "github" } } }, "shouldTranslate" : false }, "v9k-wu-2xL.placeholderString" : { "comment" : "Class = \"NSTextFieldCell\"; placeholderString = \"empty log\"; ObjectID = \"v9k-wu-2xL\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "empty log" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खाली लॉग" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "log vazio" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Boş Log" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "empty log" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "清空記錄" } } } }, "Vdr-fp-XzO.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Hide Others\"; ObjectID = \"Vdr-fp-XzO\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إخفاء الآخرين" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Skrýt ostatní" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Alle andere Apps verstecken" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Hide Others" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar otros" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Masquer les autres" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסתר אחרים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अन्य को छिपाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nascondi le altre finestre" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ほかを非表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "기타 가리기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verberg anderen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar outros" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar outros" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Спрятать другие" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Diğerlerini Gizle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Приховати інші" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "隐藏其它" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "隱藏其他" } } } }, "Ve2-qz-vtH.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Lock on sleep\"; ObjectID = \"Ve2-qz-vtH\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "غلق عند النوم" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zamknout při uspání" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Im Schlafmodus sperren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Lock on sleep" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Bloquar al entrar en reposo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Verrouiller au passage en veille" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נעל במעבר למצב שינה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्लीप पर लॉक करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Blocca se in sospensione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "スリープ時" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "잠자기 시 잠금" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Vergrendel bij slapen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear no repouso" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear ao suspender" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заблокировать в режиме сна" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Uyku modunda kilitle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заблокувати при засипанні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "系统休眠后自动锁定" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "睡眠時鎖定" } } } }, "vfT-nF-vst.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Or use .ssh private key:\"; ObjectID = \"vfT-nF-vst\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "أو استخدم المفتاح الخاص .ssh:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nebo použijte soukromý .ssh klíč:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Oder verwenden Sie einen privaten .ssh-Schlüssel:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Or use .ssh private key:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "O use la clave privada .ssh:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ou utilisez la clé privée .ssh:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "או השתמש במפתח פרטי .ssh:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "या .ssh निजी कुंजी का उपयोग करें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Oppure usa la chiave privata .ssh:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "または、.ssh 秘密鍵を使用します。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "또는 .ssh 개인 키를 사용하십시오." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Of gebruik de .ssh-privésleutel:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "ou use a chave privada .ssh" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ou use a chave privada .ssh:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Или используйте закрытый ключ .ssh:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "veya .ssh özel anahtarını kullanın:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Або використовуйте .ssh приватний ключ:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "或使用 .ssh 私钥:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "或使用 .ssh 私密金鑰:" } } } }, "VJs-0B-jGe.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Ask commit message on note save\"; ObjectID = \"VJs-0B-jGe\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Ask commit message on note save" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Ptát se na popisek commitu při uložení poznámky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ask commit message on note save" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Ask commit message on note save" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ask commit message on note save" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ask commit message on note save" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Ask commit message on note save" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट सेव करने पर कमिट संदेश पूछें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ask commit message on note save" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Ask commit message on note save" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Ask commit message on note save" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Ask commit message on note save" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pedir mensagem de confirmação ao salvar notas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ask commit message on note save" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Запрашивать название коммита перед сохранением заметки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Not kaydetmede öneri mesajını sor" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ask commit message on note save" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在笔记保存时询问提交消息" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "儲存筆記時詢問提交訊息" } } } }, "vmV-6d-7jI.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Make Upper Case\"; ObjectID = \"vmV-6d-7jI\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "جعله حروف كبيرة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Převést na velká písmena" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Großschreiben" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Make Upper Case" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Todo en mayúsculas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Passer en capitales" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הפוך לאותיות גדולות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अपर केस बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rendi tutto Maiuscolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "大文字にする" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "대문자로 만들기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Maak Hoofdletters" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tornar maiúsculas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Transformar em Maiúsculas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "В верхний регистр" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Büyük Harf Yap" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Верхній регістр" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "变为大写" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "變更為大寫" } } } }, "vrs-XL-lyE.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Move Down in the Sidebar\"; ObjectID = \"vrs-XL-lyE\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تحرك لأسفل في الشريط الجانبي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Posunout dolů v bočním panelu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In der Seitenleiste nach unten gehen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Move Down in the Sidebar" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mover abajo en la barra lateral" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Élément suivant de la barre latérale" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עבור למטה בסרגל הצד" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "साइडबार में नीचे जाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Muovi in Basso nella Sidebar" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サイドバー項目を下に移動" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "사이드바에서 아래로 이동" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Naar beneden verplaatsen in de zijbalk" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mover para baixo no menu lateral" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Descer na barra lateral" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Перейти вниз в боковой панели" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kenar Çubuğunda Aşağıya Hareket Et" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейти вниз на бічній панелі" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在侧边栏中向下移动" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在側邊欄中下移" } } } }, "VV2-X2-moA.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"*\"; ObjectID = \"VV2-X2-moA\";", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "*" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "*" } } }, "shouldTranslate" : false }, "Vwq-Ek-K2v.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Focus on search bar when ESC is pressed\"; ObjectID = \"Vwq-Ek-K2v\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ركز على شريط البحث عند الضغط على ESC" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Stisknutím Esc přepnout do vyhledávání" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Fokus auf Suchleiste, wenn ESC gedrückt wird" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Focus on search bar when ESC is pressed" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Utilizar la barra de búsqueda al presionar la tecla ESC" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Focus on Search bar when ESC is pressed" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "התמקד בשורת החיפוש בהקשת Esc" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "ESC दबाने पर खोज बार पर ध्यान केंद्रित करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Evidenzia la barra di ricerca quando premi ESC" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Escボタンを押して検索バーに移動" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "ESC를 눌렀을 때 검색창에 초점 맞추기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Focus op zoekbalk wanneer ESC wordt ingedrukt" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Focar na barra de pesquisa quando ESC é pressionado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Focar na barra de pesquisa quando pressionar ESC" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Фокус на поиске по ESC" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "ESC tuşuna basıldığında arama çubuğuna odaklan" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Фокус на пошуку по ESC" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "按下 ESC 时聚焦于搜索栏" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "按下 ESC 時將焦點移至搜尋列" } } } }, "vZO-Tx-X1j.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Create Web Page\"; ObjectID = \"vZO-Tx-X1j\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إنشاء صفحة ويب" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vytvořit webovou stránku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Online teilen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Create Web Page" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Crear página web" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Créer une page Web" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "צור דף אינטרנט" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब पेज बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Crea pagina web" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ウェブページの作成" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "웹 페이지 만들기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Webpagina maken" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Criar página Web" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Criar página da Web" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать веб-страницу" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web Sayfası Oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Створити веб-сторінку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "创建为网页" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "建立網頁" } } } }, "w7R-OL-Nfh.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Lock Folder\"; ObjectID = \"w7R-OL-Nfh\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "قفل المجلد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zamknout složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner sperren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Lock Folder" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear carpeta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Verrouiller le dossier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "נעל תיקייה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ोल्डर लॉक करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Blocca cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォルダのロック" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "폴더 잠금" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Map vergrendelen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear pasta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Bloquear pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заблокировать директорию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasörü Kilitle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заблокувати папку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "锁定文件夹" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "鎖定資料夾" } } } }, "W48-6f-4Dl.title" : { "comment" : "Class = \"NSMenu\"; title = \"Edit\"; ObjectID = \"W48-6f-4Dl\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تعديل" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Úpravy" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bearbeiten" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Edit" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Editar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Éditer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עריכה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संपादन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Modifica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "編集" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "편집" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Edit" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Editar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Editar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Редактировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Düzzenle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Редагування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "编辑" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "編輯" } } } }, "wa2-sX-NOx.placeholderString" : { "comment" : "Class = \"NSSecureTextFieldCell\"; placeholderString = \"optional\"; ObjectID = \"wa2-sX-NOx\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اختياري" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "nepovinné" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "optional" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "optional" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "opcional" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "optionnelle" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אופציונאלי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वैकल्पिक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "opzionale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "オプション" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "선택 과목" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "optioneel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Opcional" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "opcional" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "необязательный" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "isteğe bağlı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "необов'язково" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "可选的" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "選填" } } } }, "wDP-Yg-13R.placeholderString" : { "comment" : "Class = \"NSPathCell\"; placeholderString = \"id_rsa key is not selected\"; ObjectID = \"wDP-Yg-13R\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "~/.ssh/id_rsa" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "id_rsa klíč není vybraný" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "~/.ssh/id_rsa" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "id_rsa key is not selected" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "~/.ssh/id_rsa" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "~/.ssh/id_rsa" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "~/.ssh/id_rsa" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "id_rsa कुंजी चयनित नहीं है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "~/.ssh/id_rsa" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "~/.ssh/id_rsa" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "~/.ssh/id_rsa" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "~/.ssh/id_rsa" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "id_rsa key não foi selecionado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "~/.ssh/id_rsa" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ключ id_rsa не выбран" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "id_rsa anahtarı seçilmedi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "~/.ssh/id_rsa" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "~/.ssh/id_rsa" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "未選取 id_rsa 金鑰" } } } }, "WeT-3V-zwk.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Paste and Match Style\"; ObjectID = \"WeT-3V-zwk\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "لصق ومطابقة النمط" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vložit a použít styl" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einsetzen und Stil anpassen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Paste and Match Style" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Pegar con el mismo estilo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Coller et appliquer le style" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הדבק והתאם לסגנון" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पेस्ट और मैच स्टाइल" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Incolla e abbina lo stile" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ペーストしてスタイルを合わせる" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "붙여넣고 스타일 일치시킴" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Plak en pas stijl aan" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Colar e combinar estilo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Colar e igualar o estilo" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вставить в соответствии стилю" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yapıştır ve Stili Eşleştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вставити з відповідним стилем" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "粘贴并匹配样式" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "貼上並符合樣式" } } } }, "WHt-T4-l9c.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Hide/Show Note List\"; ObjectID = \"WHt-T4-l9c\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إخفاء / إظهار قائمة الملاحظات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Skrýt/zobrazit seznam poznámek" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notizliste an/ausblenden" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Hide/Show Note List" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar/mostrar notas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Masquer/afficher la liste de notes" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הסתר/הצג רשימת פתקים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट सूची छिपाएँ/दिखाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nascondi/mostra l'elenco delle note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ノート一覧の表示/非表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "노트 목록 숨기기/보이기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verbergen/tonen notitielijst" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar/mostar listar de notas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ocultar/mostrar lista de notas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Спрятать/показать список заметок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Not Listesini Gizle/Göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Приховати/показати нотатки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "隐藏/显示笔记列表" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "隱藏/顯示筆記清單" } } } }, "wpr-3q-Mcd.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Help\"; ObjectID = \"wpr-3q-Mcd\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مساعدة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nápověda" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Hilfe" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Help" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ayuda" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aide" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "עזרה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सहायता" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Aiuto" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ヘルプ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "도움말" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Help" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ajuda" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ajuda" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Помощь" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yardım" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Допомога" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "帮助" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "輔助說明" } } } }, "Ws8-ql-C52.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Arrange note list above editor\"; ObjectID = \"Ws8-ql-C52\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "وضع قائمة الملاحظات فوق المحرر" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Umístit seznam poznámek nad editor" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Die Notizenliste über dem Editor anzeigen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Arrange note list above editor" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Colocar la lista de notas encima del editor" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher la liste des notes au-dessus de l'éditeur" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "להציג את רשימת ההערות מעל העורך" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संपादक के ऊपर नोट्स की सूची रखें।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Posizionare l'elenco delle note sopra l'editor" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "エディタの上にメモの一覧を表示する" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "편집기 위에 메모 목록 배치하기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "De lijst met notities boven de editor plaatsen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Exibir a lista de notas acima do editor" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Colocar a lista de notas acima do editor" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Расположить список заметок над редактором" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Not listesini düzenleyicinin üstüne yerleştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розмістити список нотаток над редактором" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在编辑器上方显示备忘录列表" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "將備忘錄清單置於編輯器上方" } } } }, "WsG-JA-VQd.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Passphrase:\"; ObjectID = \"WsG-JA-VQd\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přístupová fráze:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Passphrase:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासफ्रेज:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Frase secreta:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Парольная фраза:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Parola" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "通關密語:" } } } }, "wW6-4n-sve.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Auto Rename By Title\"; ObjectID = \"wW6-4n-sve\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إعادة تسمية تلقائية بالعنوان" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Podle nadpisu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Auto rename by title" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Auto Rename By Title" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Renombrar automáticamente por título" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Selon le titre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תן שם לפי כותרת" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक द्वारा स्वतः नाम बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina automaticamente per titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タイトルをファイル名に自動反映" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "제목으로 자동 이름 바꾸기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Automatisch hernoemen op titel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Renomear automaticamente por título" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomear automaticamente por título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "По заголовку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlığa Göre Otomatik Yeniden Adlandırma" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Автоматичне перейменування за назвою" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "按标题自动重命名" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "依標題自動重新命名" } } } }, "wxM-bE-TE9.label" : { "comment" : "Class = \"NSTabViewItem\"; label = \"Git\"; ObjectID = \"wxM-bE-TE9\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Git" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गिट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Git" } } } }, "X3S-sG-ykW.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Quote\"; ObjectID = \"X3S-sG-ykW\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اقتبس" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Citát" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Zitat" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Quote" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Citar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Citation" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ציטוט" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "उद्धरण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Citazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "引用符" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "인용문" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Quote" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Aspas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Citação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Цитата" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Alıntı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Цитата" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "引用" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "引用" } } } }, "x3v-GG-iWU.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Copy\"; ObjectID = \"x3v-GG-iWU\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "نسخ" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kopírovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Kopieren" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Copy" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copiar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "העתק" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कॉपी करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copia" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "コピー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "복사" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kopieer" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Copiar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Copiar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Копировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скопіювати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "拷贝" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "複製" } } } }, "X76-X8-PbG.title" : { "comment" : "Class = \"NSViewController\"; title = \"Publish\"; ObjectID = \"X76-X8-PbG\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ينشر" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zveřejnit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Veröffentlichen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Publish" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Publicar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Publier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "לְפַרְסֵם" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रकाशित करें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Pubblicare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Publish" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "게시" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Publiceren" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Publicar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Publicar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Публикация" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yayımla" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Опублікувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "发布" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "發布" } } } }, "XBB-Sr-T1D.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Rename\"; ObjectID = \"XBB-Sr-T1D\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اعادة تسمية" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přejmenovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Umbenennen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Rename" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Renombrar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Renommer" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שנה שם" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नाम बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "名前を変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이름 변경" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hernoem" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Renomear" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomear" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeniden isimlendirmek" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейменувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重命名" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重新命名" } } } }, "xeP-Zg-vmO.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Bold:\"; ObjectID = \"xeP-Zg-vmO\";", "extractionState" : "extracted_with_value", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", "value" : "Fett:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Bold:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Negrita:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Gras :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "बोल्ड:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Grassetto:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Жирный:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kalın:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Жирний:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "粗体:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "粗體:" } } } }, "XEU-Ia-4j2.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Reset\"; ObjectID = \"XEU-Ia-4j2\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إعادة ضبط" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Resetovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Zurücksetzen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Reset" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Reiniciar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Réinitialiser" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אִתחוּל" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "रीसेट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ripristina" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リセット" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "초기화" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Resetten" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Resetar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Redefinir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сбросить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sıfırla" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скинути" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重設" } } } }, "xfW-7n-cIw.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Link\"; ObjectID = \"xfW-7n-cIw\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "رابط" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odkaz" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Link" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Link" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Enlace" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Lien" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "קישור" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लिंक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Link" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リンク" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "링크" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Link" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Link" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ligação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ссылка" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Link" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Посилання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "链接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "連結" } } } }, "xjf-5G-e5Q.title" : { "comment" : "Class = \"NSMenu\"; title = \"Show in Sidebar\"; ObjectID = \"xjf-5G-e5Q\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إظهار في الشريط الجانبي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit v bočním panelu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In der Seitenleiste anzeigen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Show in Sidebar" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar en la barra lateral" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher dans la barre latérale" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג בסרגל צד" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "साइडबार में दिखाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra nella barra laterale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サイトバーに表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "사이드 바에 표시" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon in zijbalk" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar menu lateral" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar na barra lateral" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показывать в сайдбаре" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kenar Çubuğunda Göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показувати на бічній панелі" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在侧边栏中显示" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示於側邊欄" } } } }, "xJQ-ch-Xlp.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Preview Font Size:\"; ObjectID = \"xJQ-ch-Xlp\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "معاينة حجم الخط:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Veilkost písma v náhledu:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Vorschauschrift:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Preview Font Size:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Tamaño de fuente de la previsualización:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Taille de l'aperçu :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גודל גופן תצוגה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ॉन्ट आकार पूर्वावलोकन:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Dimensione carattere:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "プレビューフォントサイズ:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "서체 크기 미리보기:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voorvertoning Lettertypegrootte:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Pré-visualização da fonte do texto:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pré-visualizar tamanho do tipo de letra:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Шрифт предпросмотра:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Önizleme Yazı Boyutu:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Шрифт прев'ю:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "预览字号:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "預覽字體大小:" } } } }, "xPh-BC-Xxl.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"History\"; ObjectID = \"xPh-BC-Xxl\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تاريخ" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Historie" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Die geschichte" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "History" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Historia" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Historique" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "היסטוריה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इतिहास" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cronologia" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "履歴" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "변경 이력" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Geschiedenis" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Histórico" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Histórico" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "История" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tarih" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Історія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "笔记历史版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "歷程記錄" } } } }, "xPz-yo-VwM.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Notes\"; ObjectID = \"xPz-yo-VwM\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ملاحظات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Poznámky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notizen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Notes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Notas" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Remarques" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פתקים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "すべて" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "모든 노트" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Notes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Notas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "notas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заметки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notlar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нотатки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "笔记" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "筆記" } } } }, "xrE-MZ-jX0.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Speech\"; ObjectID = \"xrE-MZ-jX0\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "كلام" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Řeč" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sprachausgabe" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Speech" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Habla" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Langage" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הקראה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "भाषण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Voce" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "スピーチ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "말하기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Spraak" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Fala" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ditar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Диктовка" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Konuşma" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Диктування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "语音" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "語音" } } } }, "xSO-lW-k8f.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Reveal in Finder\"; ObjectID = \"xSO-lW-k8f\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فتح المجلد" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit ve Finderu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In Finder anzeigen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Reveal in Finder" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar en el Finder" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Localiser dans le Finder" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג ב-Finder" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Finder में दिखाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra nel Finder" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Finderに表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Finder에서 보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Onthul in Finder" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Revelar no Finder" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Realçar no Finder" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать в Finder" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Finder'da göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати в Finder" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在访达中显示" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示於 Finder" } } } }, "xWi-IK-gJS.placeholderString" : { "comment" : "Class = \"NSSecureTextFieldCell\"; placeholderString = \"optional\"; ObjectID = \"xWi-IK-gJS\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اختياري" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "nepovinné" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "optional" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "optional" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "opcional" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "optionnelle" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "אופציונאלי" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वैकल्पिक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "opzionale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "オプション" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "선택 과목" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "optioneel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Opcional" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "opcional" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "необязательный" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "isteğe bağlı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "необов'язково" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "可选的" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "選填" } } } }, "xX0-9b-KDq.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Code block live highlighting\"; ObjectID = \"xX0-9b-KDq\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تسليط الضوء على كتلة التعليمات البرمجية (إعادة تشغيل التطبيق مطلوب)" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Živé zvýraznění u bloku kódu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Syntax automatisch hervorheben" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Code block live highlighting" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Resaltado sintáctico de código" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Surbrillance de code" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הדגש בלוקי קוד בזמן אמת (דורש אתחול)" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोड ब्लॉक लाइव हाइलाइटिंग" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Evidenzia il blocco di codice" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "コードブロックの色付け" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "코드 영역 실시간 강조" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Codeblok live markering" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Destacar em tempo real o bloco de código" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Realçar bloco de código" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Живая подсветка кода" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kod bloğu canlı vurgulama" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Миттєве підсвічування коду" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "代码块实时高亮显示" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "程式碼區塊即時突顯" } } } }, "xxB-ic-LFu.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"None\"; ObjectID = \"xxB-ic-LFu\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "بلا" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Žádné" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "None" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "None" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ninguno" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aucun" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "ללא" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोई भी नहीं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nessuno" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "なし" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "없음" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Geen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nada" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nenhum" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Не использовать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Hiçbiri" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Немає" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無" } } } }, "xyK-fE-4CD.placeholderString" : { "comment" : "Class = \"NSSecureTextFieldCell\"; placeholderString = \"passphrase\"; ObjectID = \"xyK-fE-4CD\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "přístupová fráze" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "passphrase" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासफ्रेज" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "frase secreta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Parola" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "passphrase" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "通關密語" } } } }, "Xz5-n4-O0W.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Find…\"; ObjectID = \"Xz5-n4-O0W\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ابحث ..." } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat…" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Suchen …" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Find…" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Buscar…" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Rechercher…" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "חפש..." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खोजे..." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Trova…" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "検索…" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "찾기..." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zoeken…" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Procurar..." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar..." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Найти…" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bul…" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Знайти..." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "查找…" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "尋找…" } } } }, "y4W-TV-iZ3.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Toggle Container\"; ObjectID = \"y4W-TV-iZ3\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Toggle Container" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zapnout/vypnout kontejner" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Toggle Container" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Toggle Container" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Toggle Container" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Toggle Container" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Toggle Container" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टॉगल कंटेनर" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Toggle Container" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Toggle Container" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Toggle Container" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toggle Container" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Alternar Container" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Toggle Container" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Изменить контейнер" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Konteyneri Aç/Kapat" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Toggle Container" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "切换容器" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "切換容器" } } } }, "y5T-sb-gw4.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Autoname by Title (30s)\"; ObjectID = \"y5T-sb-gw4\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "needs_review", "value" : "التسمية التلقائية حسب العنوان" } }, "cs" : { "stringUnit" : { "state" : "needs_review", "value" : "Automatické pojmenování podle názvu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Automatische Benennung nach Titel" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Autoname by Title (30s)" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Asignación automática de nombres por título" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dénomination automatique par titre" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מתן שמות אוטומטית לפי כותרת" } }, "hi" : { "stringUnit" : { "state" : "needs_review", "value" : "शीर्षक के अनुसार स्वचालित नामकरण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Denominazione automatica per titolo" } }, "ja" : { "stringUnit" : { "state" : "needs_review", "value" : "タイトルによる自動命名" } }, "ko" : { "stringUnit" : { "state" : "needs_review", "value" : "제목별 자동 이름 지정" } }, "nl-NL" : { "stringUnit" : { "state" : "needs_review", "value" : "Automatische naamgeving op titel" } }, "pt-BR" : { "stringUnit" : { "state" : "needs_review", "value" : "Nomeação automática por título" } }, "pt-PT" : { "stringUnit" : { "state" : "needs_review", "value" : "Nomeação automática por título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Автоименование по заголовку (30с)" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlığa Göre Otomatik Adlandırma" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Автоіменування за заголовком (30c)" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "按标题自动命名" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "依標題自動命名 (30s)" } } } }, "y7T-Rc-etT.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Name\"; ObjectID = \"y7T-Rc-etT\"; Note = \"#bc-ignore!\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "jméno" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Name" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नाम" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İsim" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Name" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "" } } }, "shouldTranslate" : false }, "ybG-au-Sf4.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Git repository\"; ObjectID = \"ybG-au-Sf4\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "Git repository" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Git repozitář" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Git repository" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Git repository" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Git repository" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Git repository" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "Git repository" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गिट रिपोजिटरी" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Git repository" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Git repository" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Git repository" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Git repository" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Repositório Git" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Git repository" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Git репозиторий" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Git deposu" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Git repository" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Git repository" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Git 儲存庫" } } } }, "YEy-JH-Tfz.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Find and Replace…\"; ObjectID = \"YEy-JH-Tfz\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "بحث واستبدال ..." } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat a nahradit…" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Suchen und ersetzen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Find and Replace…" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Buscar y reemplazar…" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Rechercher et remplacer…" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מצא והחלף..." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खोजे और बदलें…" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Trova e Sostituisci…" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "検索と置換…" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "찾기 및 대치..." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zoeken en Vervangen…" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Procurar e substituir..." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar e Substituir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Найти и заменить…" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bul ve Değiştir…" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Знайти та замінити..." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "查找和替换…" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "尋找與取代…" } } } }, "yMK-aK-OV2.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Move Selected Lines Up\"; ObjectID = \"yMK-aK-OV2\";", "extractionState" : "extracted_with_value", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", "value" : "Ausgewählte Zeilen nach oben verschieben" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Move Selected Lines Up" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mover líneas seleccionadas hacia arriba" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déplacer les lignes sélectionnées vers le haut" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "चुनी हुई पंक्तियों को ऊपर ले जाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sposta righe selezionate verso l'alto" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переместить выбранные строки вверх" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seçili Satırları Yukarı Taşı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перемістити вибрані рядки вгору" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "选中行向上移动" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "將選取的行上移" } } } }, "yMX-hz-B8Y.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"__\"; ObjectID = \"yMX-hz-B8Y\";", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "__" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "__" } } }, "shouldTranslate" : false }, "yNE-Yn-AWj.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Quick note:\"; ObjectID = \"yNE-Yn-AWj\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ملاحظة سريعة:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Rychlá poznámka:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Kurze Anmerkung:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Quick note:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nota rápida:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Note rapide:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הערה מהירה:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "त्वरित नोट:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nota veloce:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "クイックノート:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "빠른 메모:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Snelle notitie:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nota rápida:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nota rápida:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Быстрая заметка:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kısa not:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Швидка нотатка:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "速记:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "快速筆記:" } } } }, "Ynk-f8-cLZ.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Start Speaking\"; ObjectID = \"Ynk-f8-cLZ\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "بدء التحدث" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Spustit předčítání" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sprachausgabe starten" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Start Speaking" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Iniciar locución" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Démarrer la diction" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "התחל להקריא" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बोलना शुरू करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Avvia Lettura" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "読み上げを開始" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "말하기 시작" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Start met Praten" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Começar a falar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Começar Ditado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Начать диктовку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Konuşmaya Başla" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Почати диктування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "开始朗读" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "開始朗讀" } } } }, "You-zo-rbl.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Pin/Unpin\"; ObjectID = \"You-zo-rbl\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "تثبيت / فك التثبيت" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Připnout/odepnout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Fixieren/Loslösen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Pin/Unpin" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Anclar/desanclar" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Épingler/Désépingler" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצמד/בטל הצמדה" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पिन/अनपिन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Fissa/Sblocca" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "メモをピンで固定/ピン固定の解除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "고정/고정 해제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Pin/VerwijderPin" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Fixar/Desafixar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Fixar/soltar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Закрепить/открепить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sabitle/Sabitlemeyi kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Закріпити/відкріпити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "置顶/取消置顶" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "釘選/取消釘選" } } } }, "ypO-g0-1Xo.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Automatically insert closing braces and close quotes\"; ObjectID = \"ypO-g0-1Xo\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إدراج تلقائي لغلق الاقواس والاقتباسات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Automaticky zavírat závorky a uvozovky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Klammern und Anführungszeichen automatisch einfügen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Automatically insert closing braces and close quotes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cerrar corchetes y comillas automáticamente" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Parenthèses et guillemets fermants automatiques" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הכנס באופן אוטומטי סוגריים ומרכאות סוגרים" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्वचालित रूप से समापन ब्रेसिज़ और समापन उद्धरण सम्मिलित करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Aggiungi in automatico parentesi e virgolette di chiusura" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "自動的に閉じ括弧や右引用符を挿入" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "자동으로 괄호와 인용 닫기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Automatisch invoegen sluithaken en sluit aanhalingstekens " } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Fechar automaticamente aspas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Fechar automáticamente parêntesis e aspas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Автоматически закрывать скобки и кавычки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Otomatik olarak kapanış parantezlerini ve kapanış tırnak işaretlerini ekle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Автоматично закривати дужки та лапки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "自动补全右括号和右引号" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "自動插入結尾括號與引號" } } } }, "yVt-tz-tMy.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Username:\"; ObjectID = \"yVt-tz-tMy\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "اسم المستخدم:" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Uživatelské jméno:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Benutzername:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Username:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nombre de usuario:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nom d'utilisateur:" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "שם משתמש:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "उपयोगकर्ता नाम:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nome utente:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ユーザー名:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "사용자 이름:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Gebruikersnaam:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nome de usuário: " } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nome de usuário:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Имя:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kullanıcı Adı:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ім'я користувача:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "用户名:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "使用者名稱:" } } } }, "yx6-SW-3vn.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Image or File\"; ObjectID = \"yx6-SW-3vn\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "صورة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Obrázek nebo soubor" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bild" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Image or File" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Imagen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Image ou fichier" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "תמונה או קובץ" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "छवि या फ़ाइल" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Immagine o file" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "画像またはファイル" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이미지" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Afbeelding of bestand" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Imagem ou arquivo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Imagem ou ficheiro" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Изображение или файл" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Resim veya Dosya" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зображення або файл" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "图像" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "圖片或檔案" } } } }, "z6F-FW-3nz.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Show Substitutions\"; ObjectID = \"z6F-FW-3nz\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "مشاهدة التبديلات" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit záměny" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ersetzungen einblenden" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Show Substitutions" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar substituciones" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher les Substitutions" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג החלפות" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रतिस्थापन दिखाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra Sostituzioni" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "自動置換を表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "대체 보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon Vervangingen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar substituições" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar Substituições" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать замены" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yerine Geçenleri Göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати заміни" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "显示替换" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示替代" } } } }, "zcY-e5-gKs.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"Open in External Editor:\"; ObjectID = \"zcY-e5-gKs\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "فتح في محرر خارجي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Otevřít v externím editoru:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In externem Editor öffnen:" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Open in External Editor:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Abrir en editor externo:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir dans un éditeur externe :" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "פתח בעורך חיצוני:" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बाहरी संपादक में खोलें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apri in un editor esterno:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "外部エディタで開く:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "외부 편집기에서 열기:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Open in externe editor:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Abrir em editor externo:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Abrir no editor externo:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Внешний редактор:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Harici Düzenleyicide Aç:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відкрити в редакторі:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在外部编辑器中打开:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在外部編輯器開啟:" } } } }, "zd2-Rs-DPm.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"Force Delete\"; ObjectID = \"zd2-Rs-DPm\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "حذف اجباري" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vynutit smazání" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Force Delete" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Force Delete" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Forzar eliminación" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Forcer la suppression" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "מחק מיידית" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बलपूर्वक हटाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Forza Cancellazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "強制的に削除" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "강제 삭제" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Forceer verwijderen" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Forçar deletar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Forçar Apagar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Принудительное удаление" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Zorla Sil" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Примусово видалити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "强制删除" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "強制刪除" } } } }, "ZDl-8b-mam.title" : { "comment" : "Class = \"NSTextFieldCell\"; title = \"10\"; ObjectID = \"ZDl-8b-mam\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "3" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "10" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "3" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "10" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "3" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "3" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "3" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "10" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "3" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "3" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "3" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "3" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "10" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "3" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "10" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "10" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "3" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "3" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "10" } } } }, "Ze0-SZ-STw.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Back up manually\"; ObjectID = \"Ze0-SZ-STw\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "قم بعمل نسخة احتياطية يدويًا" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zálohovat ručně" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Manuell sichern" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Back up manually" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copia de seguridad manual" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sauvegarder manuellement" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "גבה באופן ידני" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मैन्युअल रूप से बैकअप लें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Esegui il backup manualmente" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "手動バックアップ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "수동으로 백업" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Back-up handmatig" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Backup manualmente" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Cópia de segurança manual" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Резервные копии вручную" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Manuel olarak yedekleyin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вручну" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "手动备份" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "手動備份" } } } }, "ZqK-gu-KdQ.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Note auto selection when body text matched\"; ObjectID = \"ZqK-gu-KdQ\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "لاحظ التحديد التلقائي عند مطابقة النص الأساسي" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Automaticky vybrat poznámku při shodě textu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notiz automatisch wählen, wenn der Text übereinstimmt" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Note auto selection when body text matched" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Seleccionar la nota automáticamente cuando el texto coincida" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sélec. auto de note lorsqu'un corps correspond" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "בחירה אוטומטית בפתקים כשגוף הטקסט תואם" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मुख्य पाठ मिलने पर नोट का स्वतः चयन करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Seleziona la nota che contiente nel corpo il testo cercato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "検索でヒットしたノートを自動で選択する" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "본문 텍스트 발견 시 노트 자동 선택" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Selecteer notitie automatisch als de hoofdtekst overeenkomt" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Seleção automática de notas quando o texto do corpo coincide" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Auto-seleccionar nota quando o texto corresponde" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Выбирать заметку, если тело соответствует запросу" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Gövde metni eşleştiğinde otomatik seçimi not edin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Автовибір нотатки при збігу тексту" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "正文文本匹配时自动选择笔记" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "內文符合時自動選取筆記" } } } }, "zRJ-Z2-Lsa.title" : { "comment" : "Class = \"NSMenu\"; title = \"Move Item\"; ObjectID = \"zRJ-Z2-Lsa\";", "extractionState" : "extracted_with_value", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", "value" : "Element verschieben" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Move Item" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mover elemento" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déplacer l'élément" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "आइटम हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sposta elemento" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переместить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Öğeyi Taşı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перемістити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "移动项目" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "移動項目" } } } }, "Zzw-01-JH7.title" : { "comment" : "Class = \"NSButtonCell\"; title = \"Show notes in \\\"Notes\\\" and \\\"Todo\\\" lists\"; ObjectID = \"Zzw-01-JH7\";", "extractionState" : "extracted_with_value", "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "إظهار الملاحظات في قائمة \"ملاحظات\" و \"قائمة المهام\"" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit poznámky v seznamech „Poznámky“ a „Úkoly“" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notizen in den Listen \"Notizen\" und \"Todo\" anzeigen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Show notes in \"Notes\" and \"Todo\" lists" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostras las notas en las listas \"Notas\" y \"Pendientes\"" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Voir les notes dans \"Notes\" et \"Tâches\"" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "הצג פתקים ברשימות ״פתקים״ ו-״מטלות״" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "\"नोट्स\" और \"टुडू\" सूचियों में नोट्स दिखाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra le note negli elenchi \"Note\" e \"Da Fare\"" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "\"すべて\" と \"タスク\" にノートを表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "\"모든 노트\"와 \"할 일\"에 있는 노트 보기" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon notities in \"Notes\" en \"Todo\" lijsten" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar notas em \\\"Notes\\\" and \\\"Todo\\\" listas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar notas nas listas \"Notas\" e \"Tarefas\"" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показывать заметки в общих" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notları “Notlar” ve “Yapılacaklar” listelerinde gösterme" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показувати у \"Нотатках\" і \"Завданнях\"" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在\"笔记\"和\"待办事项\"列表中显示备注" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在「筆記」與「待辦事項」清單中顯示筆記" } } } } }, "version" : "1.0" } ================================================ FILE: FSNotes Info (Notarized).plist ================================================ ATSApplicationFontsPath . CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName FSNotes CFBundleDocumentTypes CFBundleTypeExtensions etp CFBundleTypeIconFile EncryptedTextPack CFBundleTypeMIMETypes CFBundleTypeName Encrypted Text Pack CFBundleTypeRole Editor LSHandlerRank Owner LSItemContentTypes es.fsnot.etp.package LSTypeIsPackage 0 NSDocumentClass CFBundleTypeExtensions textbundle CFBundleTypeIconFile TextBundle CFBundleTypeName TextBundle CFBundleTypeRole Editor LSHandlerRank Default LSItemContentTypes org.textbundle.package LSTypeIsPackage 1 NSDocumentClass CFBundleTypeExtensions md markdown CFBundleTypeIconFile Markdown CFBundleTypeName Markdown CFBundleTypeRole Editor LSHandlerRank Default LSItemContentTypes net.daringfireball.markdown CFBundleTypeExtensions rtf CFBundleTypeIconFile RTF CFBundleTypeName Rich Text CFBundleTypeRole Editor LSHandlerRank Alternate LSItemContentTypes public.rtf CFBundleTypeExtensions txt plain-text CFBundleTypeIconFile Text CFBundleTypeName Text CFBundleTypeRole Editor LSHandlerRank Alternate LSItemContentTypes public.plain-text CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName FSNotes CFBundlePackageType APPL CFBundleShortVersionString $(MARKETING_VERSION) CFBundleURLTypes CFBundleTypeRole Viewer CFBundleURLIconFile makeNote CFBundleURLName co.fluder.fsnotes CFBundleURLSchemes fsnotes CFBundleTypeRole Viewer CFBundleURLIconFile makeNote CFBundleURLName co.fluder.fsnotes CFBundleURLSchemes nv CFBundleTypeRole Viewer CFBundleURLIconFile makeNote CFBundleURLName co.fluder.fsnotes CFBundleURLSchemes nvalt CFBundleVersion $(CURRENT_PROJECT_VERSION) LSApplicationCategoryType public.app-category.productivity LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement NSAppTransportSecurity NSAllowsArbitraryLoads NSHumanReadableCopyright Copyright © 2017-2022 Oleksandr Hlushchenko. All rights reserved. NSMainStoryboardFile Main NSPrincipalClass NSApplication NSUbiquitousContainers iCloud.co.fluder.fsnotes NSUbiquitousContainerIsDocumentScopePublic NSUbiquitousContainerName FSNotes NSUbiquitousContainerSupportedFolderLevels One NSUserActivityTypes es.fsnot.handoff-open-note UTExportedTypeDeclarations UTTypeConformsTo public.data UTTypeDescription Encrypted Text Pack UTTypeIconFile EncryptedTextPack UTTypeIdentifier es.fsnot.etp.package UTTypeReferenceURL https://fsnot.es UTTypeTagSpecification public.filename-extension etp public.mime-type UTTypeConformsTo com.apple.package UTTypeDescription TextBundle UTTypeIconFile TextBundle UTTypeIdentifier org.textbundle.package UTTypeReferenceURL http://www.textbundle.org UTTypeTagSpecification public.filename-extension textbundle UTTypeConformsTo public.plain-text UTTypeDescription Markdown UTTypeIconFile Markdown UTTypeIdentifier net.daringfireball.markdown UTTypeTagSpecification public.filename-extension md markdown public.mime-type text/markdown UTTypeConformsTo public.text UTTypeDescription Plain Text UTTypeIconFile Text UTTypeIdentifier public.plain-text UTTypeTagSpecification public.filename-extension txt public.mime-type text/plain UTTypeConformsTo public.text UTTypeDescription Rich Text UTTypeIconFile RTF UTTypeIdentifier public.rtf UTTypeTagSpecification public.filename-extension rtf public.mime-type text/rtf ================================================ FILE: FSNotes iOS/.bartycrouch.toml ================================================ [update] tasks = ["interfaces", "code", "transform", "normalize"] [update.interfaces] paths = ["."] defaultToBase = true ignoreEmptyStrings = false unstripped = false [update.code] codePaths = ["."] localizablePaths = ["."] defaultToKeys = true additive = false unstripped = false plistArguments = true [update.transform] codePaths = ["."] localizablePaths = ["."] transformer = "foundation" supportedLanguageEnumPath = "." typeName = "BartyCrouch" translateMethodName = "translate" [update.normalize] paths = ["."] sourceLocale = "en" harmonizeWithSource = true sortByKeys = true [lint] paths = ["."] duplicateKeys = false emptyValues = true ================================================ FILE: FSNotes iOS/AppDelegate.swift ================================================ // // AppDelegate.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 1/29/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // // // AppDelegate.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 1/29/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit import CoreData @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { public static var gitVC = [String: GitViewController]() public static var gitProgress: GitProgress? // MARK: Static Properties static let applicationShortcutUserInfoIconKey = "applicationShortcutUserInfoIconKey" func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Setup dynamic shortcuts let newDocument = NSLocalizedString("New Note", comment: "") let shortcutNew = UIMutableApplicationShortcutItem( type: ShortcutIdentifier.makeNew.type, localizedTitle: newDocument, localizedSubtitle: "", icon: UIApplicationShortcutIcon(type: .compose), userInfo: nil ) let saveClipboard = NSLocalizedString("Save Clipboard", comment: "") let shortcutNewClipboard = UIMutableApplicationShortcutItem( type: ShortcutIdentifier.clipboard.type, localizedTitle: saveClipboard, localizedSubtitle: "", icon: UIApplicationShortcutIcon(type: .add), userInfo: nil ) let search = NSLocalizedString("Search or Create", comment: "") let shortcutSearch = UIMutableApplicationShortcutItem( type: ShortcutIdentifier.search.type, localizedTitle: search, localizedSubtitle: "", icon: UIApplicationShortcutIcon(type: .search), userInfo: nil ) application.shortcutItems = [shortcutNew, shortcutNewClipboard, shortcutSearch] return true } // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { // Called when the user discards a scene session. } func applicationWillTerminate(_ application: UIApplication) { UserDefaultsManagement.crashedLastTime = false let temp = NSTemporaryDirectory() let encryption = URL(fileURLWithPath: temp).appendingPathComponent("Encryption") try? FileManager.default.removeItem(at: encryption) let webkitPreview = URL(fileURLWithPath: temp).appendingPathComponent("wkPreview") try? FileManager.default.removeItem(at: webkitPreview) let imagesPreview = URL(fileURLWithPath: temp).appendingPathComponent("ThumbnailsBig") try? FileManager.default.removeItem(at: imagesPreview) Storage.shared().saveProjectsCache() print("Termination end, crash status: \(UserDefaultsManagement.crashedLastTime)") } func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { if let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents").standardized, !FileManager.default.fileExists(atPath: iCloudDocumentsURL.path, isDirectory: nil) { do { try FileManager.default.createDirectory(at: iCloudDocumentsURL, withIntermediateDirectories: true, attributes: nil) } catch { print("Home directory creation: \(error)") } } return true } // MARK: - Static Helper Methods public static func getGitVC(for project: Project) -> GitViewController { if let gitVC = AppDelegate.gitVC[project.settingsKey] { return gitVC } let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil) let gvc = storyBoard.instantiateViewController(withIdentifier: "gitSettingsViewController") as! GitViewController gvc.setProject(project) AppDelegate.gitVC[project.settingsKey] = gvc return gvc } public static func getGitVCOptional(for project: Project) -> GitViewController? { if let gitVC = AppDelegate.gitVC[project.settingsKey] { return gitVC } return nil } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Colors/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Colors/fsColor.colorset/Contents.json ================================================ { "colors" : [ { "color" : { "color-space" : "srgb", "components" : { "alpha" : "1.000", "blue" : "0.850", "green" : "0.600", "red" : "0.080" } }, "idiom" : "universal" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "alpha" : "1.000", "blue" : "0.850", "green" : "0.600", "red" : "0.080" } }, "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Editor/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Editor/checkbox.imageset/Contents.json ================================================ { "images" : [ { "filename" : "checkbox.png", "idiom" : "universal", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "checkbox_white.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "checkbox@2x.png", "idiom" : "universal", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "checkbox_white@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "checkbox@3x.png", "idiom" : "universal", "scale" : "3x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "checkbox_white@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Editor/checkbox_empty.imageset/Contents.json ================================================ { "images" : [ { "filename" : "checkbox_empty.png", "idiom" : "universal", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "checkbox_empty_white 1.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "checkbox_empty@2x.png", "idiom" : "universal", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "checkbox_empty_white@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "checkbox_empty@3x.png", "idiom" : "universal", "scale" : "3x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "checkbox_empty_white@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Icons/AppIconClassic-2025.imageset/Contents.json ================================================ { "images" : [ { "filename" : "classic.png", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Icons/AppIconModern.imageset/Contents.json ================================================ { "images" : [ { "filename" : "classic-iOS-Default-1024x1024@1x.png", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Icons/AppIconNy-2026.imageset/Contents.json ================================================ { "images" : [ { "filename" : "ny-2026-iOS-Default-1024x1024@1x.png", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Icons/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/LaunchScreenImage.imageset/Contents.json ================================================ { "images" : [ { "filename" : "icon-1024-1024-tint.png", "idiom" : "universal", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "app-icon-dylanseegerDarkFull.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "icon-1024-1024-tint 1.png", "idiom" : "universal", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "app-icon-dylanseegerDarkFull 1.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "icon-1024-1024-tint 2.png", "idiom" : "universal", "scale" : "3x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "app-icon-dylanseegerDarkFull 2.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Sidebar/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Sidebar/sidebar_archive.imageset/Contents.json ================================================ { "images" : [ { "filename" : "Archive-76.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "Archive-76@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "Archive-83.5@2x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Sidebar/sidebar_inbox.imageset/Contents.json ================================================ { "images" : [ { "filename" : "1172241_inbox_letter_mail_mailbox_icon-76.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "1172241_inbox_letter_mail_mailbox_icon-76@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "1172241_inbox_letter_mail_mailbox_icon-83.5@2x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Sidebar/sidebar_notes.imageset/Contents.json ================================================ { "images" : [ { "filename" : "Notes-76.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "Notes-76@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "Notes-83.5@2x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Sidebar/sidebar_project.imageset/Contents.json ================================================ { "images" : [ { "filename" : "folder-77.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "folder-76@2x-1.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "folder-76@2x-1 1.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Sidebar/sidebar_project_encrypted_locked.imageset/Contents.json ================================================ { "images" : [ { "filename" : "locked@74.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "locked@148.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "locked@222.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Sidebar/sidebar_project_encrypted_unlocked.imageset/Contents.json ================================================ { "images" : [ { "filename" : "unlocked@74.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "unlocked@148.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "unlocked@222.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Sidebar/sidebar_tag.imageset/Contents.json ================================================ { "images" : [ { "filename" : "tag-76.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "tag-76@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "tag-76@2x 1.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Sidebar/sidebar_todo.imageset/Contents.json ================================================ { "images" : [ { "filename" : "t2-76.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "t2-76@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "t2-83.5@2x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Sidebar/sidebar_trash.imageset/Contents.json ================================================ { "images" : [ { "filename" : "Trash-76.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "Trash-76@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "Trash-83.5@2x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Sidebar/sidebar_untagged.imageset/Contents.json ================================================ { "images" : [ { "filename" : "1172268_tag_tags_icon-76.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "1172268_tag_tags_icon-76@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "1172268_tag_tags_icon-83.5@2x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Sidebar Actions/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Sidebar Actions/gitSettings.imageset/Contents.json ================================================ { "images" : [ { "filename" : "git-20.png", "idiom" : "universal", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "git-20 1.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "git-20@2x.png", "idiom" : "universal", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "git-20@2x 1.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "git-20@3x.png", "idiom" : "universal", "scale" : "3x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "git-20@3x 1.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/codeBlockAsset.imageset/Contents.json ================================================ { "images" : [ { "filename" : "codeblock.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "codeblock-1.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "codeblock-2.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/numbered_list.imageset/Contents.json ================================================ { "images" : [ { "filename" : "numbered_list.png", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/ordered_list.imageset/Contents.json ================================================ { "images" : [ { "filename" : "ordered_list.png", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/pictureAsset.imageset/Contents.json ================================================ { "images" : [ { "filename" : "picture@1x.png", "idiom" : "universal", "scale" : "1x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "picture@1x-white.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "picture@2x.png", "idiom" : "universal", "scale" : "2x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "picture@2x-white.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "picture@3x.png", "idiom" : "universal", "scale" : "3x" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "picture@3x-white.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/quote.imageset/Contents.json ================================================ { "images" : [ { "filename" : "quote.png", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/redo.imageset/Contents.json ================================================ { "images" : [ { "filename" : "redo.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "redo@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "redo@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/toolbarBold.imageset/Contents.json ================================================ { "images" : [ { "filename" : "icons8-bold-52-20.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "icons8-bold-52-20@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "icons8-bold-52-20@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/toolbarHeader.imageset/Contents.json ================================================ { "images" : [ { "filename" : "type-h1-icon_1-20.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "type-h1-icon_1-20@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "type-h1-icon_1-20@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/toolbarImage.imageset/Contents.json ================================================ { "images" : [ { "filename" : "icons8-image-96-20.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "icons8-image-96-20@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "icons8-image-96-20@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/toolbarIndentLeft.imageset/Contents.json ================================================ { "images" : [ { "filename" : "right-indent-20.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "right-indent-20@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "right-indent-20@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/toolbarIndentRight.imageset/Contents.json ================================================ { "images" : [ { "filename" : "left-indent-20.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "left-indent-20@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "left-indent-20@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/toolbarItalic.imageset/Contents.json ================================================ { "images" : [ { "filename" : "italic-text-option-interface-symbol-20.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "italic-text-option-interface-symbol-20@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "italic-text-option-interface-symbol-20@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/toolbarTag.imageset/Contents.json ================================================ { "images" : [ { "filename" : "iconfinder_20-blue_fee-label-price-tag_4488787-20.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "iconfinder_20-blue_fee-label-price-tag_4488787-20@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "iconfinder_20-blue_fee-label-price-tag_4488787-20@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/toolbarTodo.imageset/Contents.json ================================================ { "images" : [ { "filename" : "icons8-todo-list-96-20.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "icons8-todo-list-96-20@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "icons8-todo-list-96-20@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/toolbarWiki.imageset/Contents.json ================================================ { "images" : [ { "filename" : "icons8-documents-96-20.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "icons8-documents-96-20@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "icons8-documents-96-20@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Toolbar/undo.imageset/Contents.json ================================================ { "images" : [ { "filename" : "undo.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "undo@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "undo@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Touch Bar/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Touch Bar/Image.imageset/Contents.json ================================================ { "images" : [ { "filename" : "image.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "image@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "image@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Touch Bar/bold.imageset/Contents.json ================================================ { "images" : [ { "filename" : "bold.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "bold@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "bold@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Touch Bar/codeblock.imageset/Contents.json ================================================ { "images" : [ { "filename" : "codeblock.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "codeblock 1.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "codeblock 2.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Touch Bar/indent.imageset/Contents.json ================================================ { "images" : [ { "filename" : "indent.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "indent@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "indent@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Touch Bar/italic.imageset/Contents.json ================================================ { "images" : [ { "filename" : "italic.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "italic@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "italic@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Touch Bar/tb_link.imageset/Contents.json ================================================ { "images" : [ { "filename" : "tb_link.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "tb_link 1.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "tb_link 2.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Touch Bar/todo.imageset/Contents.json ================================================ { "images" : [ { "filename" : "todo.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "todo@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "todo@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/Assets.xcassets/Touch Bar/unindent.imageset/Contents.json ================================================ { "images" : [ { "filename" : "unindent.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "unindent@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "unindent@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: FSNotes iOS/DatePickerViewController.swift ================================================ // // DatePickerViewController.swift // FSNotes iOS // // Created by Александр on 01.02.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // import Foundation import UIKit class DatePickerViewController: UIViewController { @IBOutlet weak var datePicker: UIDatePicker! @IBOutlet weak var navigationBar: UINavigationBar! @IBOutlet weak var bottomSafeView: UIView! @IBOutlet weak var navItem: UINavigationItem! public var notes: [Note]? override func viewDidLoad() { super.viewDidLoad() navigationBar.barTintColor = UIColor.sidebar navigationBar.tintColor = UIColor.mainTheme navigationBar.backgroundColor = UIColor.sidebar bottomSafeView.backgroundColor = UIColor.sidebar if #available(iOS 14.0, *) { datePicker.preferredDatePickerStyle = .inline } if let date = notes?.first?.creationDate { datePicker.date = date } initButtons() } @IBAction func saveDate(_ sender: Any) { guard let notes = self.notes else { return } for note in notes { _ = note.setCreationDate(date: datePicker.date) } DispatchQueue.main.async { UIApplication.getVC().notesTable.reloadRows(notes: notes) } self.notes = nil dismiss(animated: true) } @IBAction func closeController(_ sender: Any) { dismiss(animated: true) } private func initButtons() { let leftString = NSLocalizedString("Cancel", comment: "") navItem.leftBarButtonItem = UIBarButtonItem(title: leftString, style: .plain, target: self, action: #selector(closeController)) let saveBarButton = UIBarButtonItem(title: NSLocalizedString("Update", comment: ""), style: .plain, target: self, action: #selector(saveDate)) navItem.rightBarButtonItem = saveBarButton } } ================================================ FILE: FSNotes iOS/EditorViewController+QuickLook.swift ================================================ // // EditorViewController+QuickLook.swift // FSNotes iOS // // Created by Oleksandr Hlushchenko on 05.05.2024. // Copyright © 2024 Oleksandr Hlushchenko. All rights reserved. // import QuickLook extension EditorViewController: QLPreviewControllerDataSource { func numberOfPreviewItems(in controller: QLPreviewController) -> Int { return 1 } func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> any QLPreviewItem { guard let quickLookURL = quickLookURL else { fatalError("File URL is nil") } return quickLookURL as QLPreviewItem } func quickLook(url: URL) { let previewController = QLPreviewController() previewController.dataSource = self quickLookURL = url navigationController?.pushViewController(previewController, animated: true) } } ================================================ FILE: FSNotes iOS/EditorViewController+Search.swift ================================================ // // EditorViewController+Search.swift // FSNotes // // Created by Oleksandr Hlushchenko on 14.01.2026. // Copyright © 2026 Oleksandr Hlushchenko. All rights reserved. // import UIKit extension EditorViewController { func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { clearHighlights() guard !searchText.isEmpty else { updateCounterLabel() return } findRanges(text: searchText) } func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { nextResult() } func scrollToCurrent() { guard !searchRanges.isEmpty else { return } let range = searchRanges[currentSearchIndex] let layoutManager = editArea.layoutManager let textContainer = editArea.textContainer let targetGlyphRange = layoutManager.glyphRange( forCharacterRange: range, actualCharacterRange: nil ) let extendedGlyphRange = NSRange( location: 0, length: targetGlyphRange.location + targetGlyphRange.length ) layoutManager.ensureLayout(forGlyphRange: extendedGlyphRange) _ = layoutManager.boundingRect( forGlyphRange: targetGlyphRange, in: textContainer ) editArea.selectedRange = range editArea.scrollRangeToVisible(range) updateCounterLabel() } func nextResult() { guard !searchRanges.isEmpty else { return } currentSearchIndex = (currentSearchIndex + 1) % searchRanges.count highlightAll() scrollToCurrent() } func prevResult() { guard !searchRanges.isEmpty else { return } currentSearchIndex = (currentSearchIndex - 1 + searchRanges.count) % searchRanges.count highlightAll() scrollToCurrent() } func highlightAll() { guard !searchRanges.isEmpty else { return } let textStorage = editArea.textStorage textStorage.beginEditing() clearHighlights() originalBackgrounds.removeAll() for (index, range) in searchRanges.enumerated() { let color = index == currentSearchIndex ? UIColor.systemOrange.withAlphaComponent(0.6) : UIColor.systemYellow.withAlphaComponent(0.4) let currentBg = textStorage.attribute(.backgroundColor, at: range.location, effectiveRange: nil) as? UIColor originalBackgrounds[range] = currentBg textStorage.addAttribute(.backgroundColor, value: color, range: range) } textStorage.endEditing() } func clearHighlights() { guard !originalBackgrounds.isEmpty else { return } let textStorage = editArea.textStorage textStorage.beginEditing() for (range, originalColor) in originalBackgrounds { if let color = originalColor { textStorage.addAttribute(.backgroundColor, value: color, range: range) } else { textStorage.removeAttribute(.backgroundColor, range: range) } } textStorage.endEditing() originalBackgrounds.removeAll() } func findRanges(text: String) { searchRanges.removeAll() currentSearchIndex = 0 let nsText = editArea.text as NSString var searchRange = NSRange(location: 0, length: nsText.length) while true { let found = nsText.range( of: text, options: .caseInsensitive, range: searchRange ) if found.location == NSNotFound { break } searchRanges.append(found) searchRange = NSRange( location: found.location + found.length, length: nsText.length - found.location - found.length ) } highlightAll() if !searchRanges.isEmpty { scrollToCurrent() } updateCounterLabel() } func updateCounterLabel() { guard let counterLabel = counterLabel else { return } if searchRanges.isEmpty { counterLabel.text = "" } else { counterLabel.text = "\(currentSearchIndex + 1)/\(searchRanges.count)" } } func showSearch() { guard let note = editArea.note else { return } if note.previewState { togglePreview() } if searchToolbar == nil { setupSearchAccessory() } keyboardAnchor?.becomeFirstResponder() searchBar?.becomeFirstResponder() originalSelectedRange = editArea.selectedRange } func hideSearch() { keyboardAnchor?.resignFirstResponder() searchBar?.resignFirstResponder() searchToolbar = nil searchBar = nil counterLabel = nil keyboardAnchor?.removeFromSuperview() keyboardAnchor = nil clearHighlights() if let range = originalSelectedRange { editArea.selectedRange = range } addToolBar(textField: editArea, toolbar: getMarkdownToolbar()) } @objc func editorSearch() { if searchBar != nil { hideSearch() } else { showSearch() } } func setupSearchAccessory() { keyboardAnchor = UITextField() keyboardAnchor?.isHidden = true view.addSubview(keyboardAnchor!) searchBar = UISearchBar() guard let searchBar = searchBar else { return } searchBar.delegate = self searchBar.placeholder = "Find" searchBar.autocapitalizationType = .none searchBar.autocorrectionType = .no searchBar.searchBarStyle = .minimal searchBar.showsCancelButton = false if let textField = searchBar.value(forKey: "searchField") as? UITextField { textField.clearButtonMode = .never } searchBar.translatesAutoresizingMaskIntoConstraints = false counterLabel = UILabel() guard let counterLabel = counterLabel else { return } counterLabel.font = UIFont.monospacedSystemFont(ofSize: 13, weight: .regular) counterLabel.textColor = .secondaryLabel counterLabel.textAlignment = .center counterLabel.translatesAutoresizingMaskIntoConstraints = false counterLabel.setContentHuggingPriority(.required, for: .horizontal) counterLabel.setContentCompressionResistancePriority(.required, for: .horizontal) counterLabel.widthAnchor.constraint(equalToConstant: 45).isActive = true let prevButton = UIButton(type: .system) prevButton.setImage(UIImage(systemName: "chevron.up"), for: .normal) prevButton.addTarget(self, action: #selector(prevTap), for: .touchUpInside) prevButton.widthAnchor.constraint(equalToConstant: 32).isActive = true let nextButton = UIButton(type: .system) nextButton.setImage(UIImage(systemName: "chevron.down"), for: .normal) nextButton.addTarget(self, action: #selector(nextTap), for: .touchUpInside) nextButton.widthAnchor.constraint(equalToConstant: 32).isActive = true let closeButton = UIButton(type: .system) closeButton.setImage(UIImage(systemName: "xmark"), for: .normal) closeButton.addTarget(self, action: #selector(closeSearch), for: .touchUpInside) closeButton.widthAnchor.constraint(equalToConstant: 32).isActive = true let prev = UIBarButtonItem(customView: prevButton) let next = UIBarButtonItem(customView: nextButton) let close = UIBarButtonItem(customView: closeButton) let searchItem = UIBarButtonItem(customView: searchBar) let counterItem = UIBarButtonItem(customView: counterLabel) searchToolbar = UIToolbar() guard let searchToolbar = searchToolbar else { return } searchToolbar.items = [ searchItem, counterItem, prev, next, close ] searchToolbar.sizeToFit() keyboardAnchor?.inputAccessoryView = searchToolbar } @objc func nextTap() { nextResult() } @objc func prevTap() { prevResult() } @objc func closeSearch() { clearHighlights() keyboardAnchor?.resignFirstResponder() searchBar?.resignFirstResponder() editArea.inputAccessoryView = nil editArea.reloadInputViews() searchToolbar = nil searchBar = nil counterLabel = nil keyboardAnchor?.removeFromSuperview() keyboardAnchor = nil self.addToolBar(textField: editArea, toolbar: self.getMarkdownToolbar()) } func openSearchWithText(_ searchText: String) { showSearch() self.searchBar?.text = searchText self.findRanges(text: searchText) } } ================================================ FILE: FSNotes iOS/EditorViewController.swift ================================================ // // EditorViewController.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 1/31/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit import AudioToolbox import MobileCoreServices import Photos import DropDown import CoreSpotlight import PhotosUI class EditorViewController: UIViewController, UITextViewDelegate, UIDocumentPickerDelegate, UIGestureRecognizerDelegate, PHPickerViewControllerDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UISearchBarDelegate { public var note: Note? public var quickLookURL: URL? private var isUndo = false private let storageQueue = OperationQueue() var inProgress = false var change = 0 public var undoBarButton: UIBarButtonItem? public var redoBarButton: UIBarButtonItem? @IBOutlet weak var editArea: EditTextView! var rowUpdaterTimer = Timer() public var tagsTimer: Timer? private let dropDown = DropDown() private var isLandscape: Bool? private var lastStyle: UIUserInterfaceStyle? // Search toolbar var keyboardAnchor: UITextField? var counterLabel: UILabel? var searchBar: UISearchBar? var searchToolbar: UIToolbar? var searchRanges: [NSRange] = [] var currentSearchIndex: Int = 0 var originalSelectedRange: NSRange? var originalBackgrounds: [NSRange: UIColor?] = [:] override func viewDidLoad() { storageQueue.maxConcurrentOperationCount = 1 storageQueue.qualityOfService = .userInitiated editArea.textContainerInset = UIEdgeInsets(top: 13, left: 10, bottom: 0, right: 10) let imageTap = SingleImageTouchDownGestureRecognizer(target: self, action: #selector(imageTapHandler(_:))) editArea.addGestureRecognizer(imageTap) let tap = SingleTouchDownGestureRecognizer(target: self, action: #selector(tapHandler(_:))) editArea.addGestureRecognizer(tap) editArea.initTextStorage() let tapGR = UITapGestureRecognizer(target: self, action: #selector(editMode)) tapGR.delegate = self tapGR.numberOfTapsRequired = 2 view.addGestureRecognizer(tapGR) editArea.imagesLoaderQueue.maxConcurrentOperationCount = 1 editArea.imagesLoaderQueue.qualityOfService = .userInteractive super.viewDidLoad() var items = [UIBarButtonItem]() items.append(UIBarButtonItem(systemImageName: "magnifyingglass", target: self, selector: #selector(editorSearch))) items.append(UIBarButtonItem.flexibleSpace()) items.append(UIBarButtonItem(systemImageName: "plus", target: self, selector: #selector(newNote))) toolbarItems = items self.addToolBar(textField: editArea, toolbar: self.getMarkdownToolbar()) NotificationCenter.default.addObserver(self, selector: #selector(themeObserver), name: UIApplication.didBecomeActiveNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(preferredContentSizeChanged), name: UIContentSizeCategory.didChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: UIDevice.orientationDidChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(refill), name: NSNotification.Name(rawValue: "es.fsnot.external.file.changed"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) editArea.keyboardDismissMode = .interactive registerForKeyboardNotifications() registerForAppStateNotifications() } @objc func rotated() { guard isLandscape != nil else { isLandscape = UIDevice.current.orientation.isLandscape return } let isLand = UIDevice.current.orientation.isLandscape if let landscape = self.isLandscape, landscape != isLand, !UIDevice.current.orientation.isFlat { isLandscape = isLand } } override func viewDidAppear(_ animated: Bool) { editArea.isScrollEnabled = true super.viewDidAppear(animated) if editArea.textStorage.length == 0 && editArea.note?.previewState == false { editArea.perform(#selector(becomeFirstResponder), with: nil, afterDelay: 0) } initLinksColor() editArea.flashScrollIndicators() initSwipes() } override func viewWillAppear(_ animated: Bool) { updateTitle() super.viewWillAppear(animated) configureNavMenu() navigationItem.largeTitleDisplayMode = .never navigationController?.setToolbarHidden(false, animated: true) navigationController?.toolbar.tintColor = UIColor.mainTheme navigationController?.navigationBar.tintColor = UIColor.mainTheme } @objc func search() { UIApplication.getVC().enableSearchFocus() self.cancel() } @objc func newNote() { UIApplication.getVC().createNote(content: "") configureNavMenu() } override func viewWillDisappear(_ animated: Bool) { editArea.endEditing(true) UIApplication.getNC()?.navigationItem.searchController = nil } override var disablesAutomaticKeyboardDismissal: Bool { return false } override var textInputMode: UITextInputMode? { if let keyboard = UserDefaultsManagement.defaultKeyboard { for mode in UITextInputMode.activeInputModes { if mode.primaryLanguage == keyboard { return mode } } } return super.textInputMode } public func updateTitle() { navigationItem.title = note?.project.label if #available(iOS 26.0, *) { navigationItem.subtitle = note?.url.lastPathComponent } } private func registerForKeyboardNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) } private func unregisterFromKeyboardNotifications() { NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil) } private func registerForAppStateNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(appDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil) } @objc private func appDidEnterBackground() { unregisterFromKeyboardNotifications() } @objc private func appWillEnterForeground() { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.registerForKeyboardNotifications() } } public func configureNavMenu() { guard let note = self.note else { return } guard let menu = UIApplication.getVC().notesTable.makeBulkMenu(editor: true, note: note) else { return } let buttonName = editArea.note?.previewState == true ? "eye.slash" : "eye" let previewBarItem = UIBarButtonItem(systemImageName: buttonName, target: self, selector: #selector(togglePreview)) previewBarItem.tag = 5 navigationItem.rightBarButtonItems = [ UIBarButtonItem(systemImageName: "ellipsis.circle", menu: menu), previewBarItem ] } public func fill(note: Note, selectedRange: NSRange? = nil, clearPreview: Bool = false, enableHandoff: Bool = true, completion: (() -> ())? = nil) { if enableHandoff { registerHandoff(for: note) } self.note = note if !note.isLoaded { note.load() } editArea.note = note if note.previewState { loadPreviewView() completion?() return } getPreviewView()?.removeFromSuperview() fillEditor(note: note, selectedRange: selectedRange) completion?() } private func fillEditor(note: Note, selectedRange: NSRange? = nil) { guard editArea != nil else { return } editArea.isNoteLoading = true editArea.initUndoRedoButons() view.backgroundColor = UIColor.dropDownColor editArea.backgroundColor = UIColor.dropDownColor if let content = note.content.mutableCopy() as? NSMutableAttributedString { editArea.attributedText = content } if let scroll = editArea.inputAccessoryView as? UIScrollView { scroll.contentOffset = .zero } editArea.delegate = self let storage = editArea.textStorage storage.updateCheckboxList() editArea.typingAttributes[.font] = UserDefaultsManagement.noteFont editArea.layoutManager.ensureLayout(for: editArea.textContainer) editArea.layoutIfNeeded() self.loadSelectedRange() if let query = self.getSearchText(), query.count > 0 { UIApplication.getVC().enableSearchFocus(string: query) DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self.openSearchWithText(query) } } } @objc public func clickOnButton() { let vc = UIApplication.getVC() guard let note = self.note else { return } vc.notesTable.actionsSheet(notes: [note], showAll: true, presentController: self) } @objc func refill() { guard let editArea = editArea else { return } initLinksColor() if let note = self.note { let keyboardIsOpen = editArea.isFirstResponder if keyboardIsOpen { editArea.endEditing(true) } fill(note: note) if keyboardIsOpen { _ = editArea.becomeFirstResponder() } } } private var keyboardFrameChangeCount = 0 @objc func keyboardWillChangeFrame(_ notification: Notification) { keyboardFrameChangeCount += 1 } func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { guard let note = self.note else { return true } tagsHandler(affectedCharRange: range, text: text) wikilinkHandler(textView: textView, text: text) deleteUnusedImages(checkRange: range) // New line if text == "\n" { let formatter = TextFormatter(textView: self.editArea, note: note) formatter.newLine() return false } // Tab if text == "\t" { let formatter = TextFormatter(textView: self.editArea, note: note) formatter.tabKey() return false } if let font = self.editArea.typingFont { editArea.typingAttributes.removeAll() editArea.typingAttributes[.font] = font } return true } private func tagsHandler(affectedCharRange: NSRange, text: String) { guard UserDefaultsManagement.inlineTags else { return } let textStorage = editArea.textStorage // close dropdown if ["", " ", "\t", "\n"].contains(text) { if !dropDown.isHidden { dropDown.hide() } return } // one char guard text.count == 1 else { return } let nextCharLocation = affectedCharRange.location + 1 let nextChar: String if editArea.selectedRange.length == 0, nextCharLocation <= textStorage.length { let nextCharRange = NSRange(location: affectedCharRange.location, length: 1) nextChar = textStorage.mutableString.substring(with: nextCharRange) } else { nextChar = " " } if affectedCharRange.location > 0 { let hashRange = NSRange(location: affectedCharRange.location - 1, length: 1) let prevChar = (textStorage.string as NSString).substring(with: hashRange) if prevChar == "#" && nextChar.isWhitespace { let filteredTags = self.getAllTags().filter { $0.lowercased().starts(with: text.lowercased()) } if filteredTags.isEmpty { dropDown.hide() } else { dropDown.dataSource = filteredTags complete(offset: hashRange.location, text: text) } return } } let parRange = textStorage.mutableString.paragraphRange(for: NSRange(location: affectedCharRange.location, length: 0)) textStorage.mutableString.enumerateSubstrings(in: parRange, options: .byWords) { word, range, _, stop in guard let word = word, affectedCharRange.location <= range.upperBound, affectedCharRange.location >= range.lowerBound, range.location > 0 else { return } let hashRange = NSRange(location: range.location - 1, length: 1) let prevChar = (textStorage.string as NSString).substring(with: hashRange) if prevChar == "#" && nextChar.isWhitespace { let searchText = word + text let filteredTags = self.getAllTags().filter { $0.lowercased().starts(with: searchText.lowercased()) } if filteredTags.isEmpty { self.dropDown.hide() } else { self.dropDown.dataSource = filteredTags self.complete(offset: hashRange.location, range: range, text: text) } stop.pointee = true } } } private func getAllTags() -> [String] { let vc = UIApplication.getVC() var projects = [Project]() if let project = Storage.shared().searchQuery.projects.first { projects.append(project) } else { projects = Storage.shared().getProjects() } let allTags = vc.sidebarTableView.getAllTags(projects: projects) return allTags } private func wikilinkHandler(textView: UITextView, text: String) { guard text.count == 1, !["\n"].contains(text) else { return } let textStorage = textView.textStorage let location = textView.selectedRange.location // Encoded offset for Emoji guard let cursor = textView.cursorDistance else { return } let parRange = textStorage.mutableString.paragraphRange(for: NSRange(location: location, length: 0)) let paragraph = textStorage.attributedSubstring(from: parRange).string guard paragraph.contains("[[") && paragraph.contains("]]"), let result = isBetweenBraces(location: cursor) else { return } let word = result.0 + text guard let titles = Storage.shared().getTitles(by: word) else { dropDown.hide() return } dropDown.dataSource = titles let range = result.1 // Decode multibyte offset for Emoji like "🇺🇦" let startIndex = textView.text.index(textView.text.startIndex, offsetBy: range.lowerBound + 2) let startRange = NSRange(startIndex...startIndex, in: textView.text) let replacementRange = NSRange(location: startRange.lowerBound, length: word.count) complete(offset: replacementRange.location, replacementRange: replacementRange) } private func isBetweenBraces(location: Int) -> (String, NSRange)? { let storage = editArea.textStorage let string = Array(storage.string) let length = storage.length var firstLeftFound = false var firstRigthFound = false var rigthFound = false var leftFound = false var i = location - 1 var j = location while i >= 0 { let char = string[i] if firstLeftFound { leftFound = char == "[" break } if char.isNewline { break } if char == "[" { firstLeftFound = true } i -= 1 } while length > j { let char = string[j] if firstRigthFound { rigthFound = char == "]" break } if char.isNewline { break } if char == "]" { firstRigthFound = true } j += 1 } var result = String() if leftFound && rigthFound { result = String(string[i...j]) result = result .replacingOccurrences(of: "[[", with: "") .replacingOccurrences(of: "]]", with: "") return (result, NSRange(i...j)) } return nil } private func complete(offset: Int? = nil, range: NSRange? = nil, text: String? = nil, replacementRange: NSRange? = nil) { var endPosition: UITextPosition = editArea.endOfDocument if let offset = offset, let position = editArea.position(from: editArea.beginningOfDocument, offset: offset) { endPosition = position } let rect = editArea.caretRect(for: endPosition) let customView = UIView(frame: CGRect(x: rect.origin.x, y: rect.origin.y + 30, width: 200, height: 0)) editArea.addSubview(customView) dropDown.cellHeight = 35 dropDown.textFont = UIFont.boldSystemFont(ofSize: 15) dropDown.backgroundColor = UIColor.dropDownColor dropDown.textColor = traitCollection.userInterfaceStyle == .dark ? UIColor.white : UIColor.gray dropDown.anchorView = customView dropDown.selectionAction = { [unowned self] (index: Int, item: String) in customView.removeFromSuperview() // WikiLinks if let replacementRange = replacementRange { editArea.undoManager?.beginUndoGrouping() self.editArea.selectedRange = replacementRange self.editArea.insertText(item) editArea.undoManager?.endUndoGrouping() return } if let range = range, let text = text { let string = self.editArea.textStorage.mutableString.substring(with: range) + text let addText = item.replacingOccurrences(of: string, with: "") self.editArea.insertText(addText + " ") } else if text != nil { let addText = String(item.dropFirst()) self.editArea.insertText(addText + " ") } else { self.editArea.insertText(item + " ") } } dropDown.show() } @objc public func scanTags() { guard UserDefaultsManagement.inlineTags else { return } guard let note = editArea.note else { return } UIApplication.getVC().sidebarTableView.loadTags(notes: [note]) if let title = note.getAutoRenameTitle() { UIApplication.getVC().notesTable.rename(note: note, to: title) UIApplication.getEVC().updateTitle() } } private func deleteUnusedImages(checkRange: NSRange) { editArea.textStorage.enumerateAttribute(.attachment, in: checkRange) { (value, range, _) in guard let meta = editArea.textStorage.getMeta(at: range.location) else { return } do { if let data = try? Data(contentsOf: meta.url) { editArea.textStorage.addAttribute(.attachmentSave, value: data, range: range) try FileManager.default.removeItem(at: meta.url) } } catch { print(error) } } } private func deleteBackwardPressed(text: String) -> Bool { if !self.isUndo, let char = text.cString(using: String.Encoding.utf8), strcmp(char, "\\b") == -92 { return true } self.isUndo = false return false } func textViewDidChangeSelection(_ textView: UITextView) { if textView.isFirstResponder { // Handoff needs update in cursor position cahnged userActivity?.needsSave = true saveSelectedRange() } } func scrollViewDidScroll(_ scrollView: UIScrollView) { saveSelectedRange() } func textViewDidChange(_ textView: UITextView) { let vc = UIApplication.getVC() //vc.cloudDriveManager?.metadataQuery.disableUpdates() guard let note = self.note else { return } // Prevent textStorage refresh in CloudDriveManager note.modifiedLocalAt = Date() self.storageQueue.cancelAllOperations() let text = self.editArea.attributedText.mutableCopy() as? NSMutableAttributedString let operation = BlockOperation() operation.addExecutionBlock { [weak self] in guard let self = self, let text = text else {return} note.save(content: text) if note.isEncrypted() && !note.isUnlocked() { DispatchQueue.main.async { self.cancel() } return } note.invalidateCache() note.loadPreviewInfo() vc.updateSpotlightIndex(notes: [note]) DispatchQueue.main.async { self.rowUpdaterTimer.invalidate() self.rowUpdaterTimer = Timer.scheduledTimer(timeInterval: 1.2, target: self, selector: #selector(self.updateCurrentRow), userInfo: nil, repeats: false) self.tagsTimer?.invalidate() self.tagsTimer = Timer.scheduledTimer(timeInterval: 2.5, target: self, selector: #selector(self.scanTags), userInfo: nil, repeats: false) } usleep(100000) } self.storageQueue.addOperation(operation) editArea.typingAttributes.removeValue(forKey: .backgroundColor) editArea.typingAttributes[.font] = UserDefaultsManagement.noteFont editArea.initUndoRedoButons() //vc.cloudDriveManager?.metadataQuery.enableUpdates() } @objc private func updateCurrentRow() { let vc = UIApplication.getVC() guard let note = self.note else { return } vc.notesTable.moveRowUp(note: note) vc.notesTable.reloadRows(notes: [note]) } func getSearchText() -> String? { if let search = UIApplication.getVC().navigationItem.searchController?.searchBar.text { return search } return nil } @objc func keyboardWillShow(notification: NSNotification) { keyboardFrameChangeCount = 0 guard let userInfo = notification.userInfo else { return } guard var keyboardFrame: CGRect = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return } keyboardFrame = view.convert(keyboardFrame, from: nil) let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .left paragraphStyle.lineSpacing = CGFloat(UserDefaultsManagement.editorLineSpacing) self.editArea.typingAttributes[.paragraphStyle] = paragraphStyle self.editArea.typingAttributes.removeValue(forKey: .link) let keyboardHeight = keyboardFrame.height let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardHeight - 66, right: 0.0) self.editArea.contentInset = contentInsets self.editArea.scrollIndicatorInsets = contentInsets } @objc func keyboardWillHide(notification: NSNotification) { let contentInsets = UIEdgeInsets.zero editArea.contentInset = contentInsets editArea.scrollIndicatorInsets = contentInsets if isKeyboardClosedManually() { // NO selection more saveSelectedRangeZero() } if searchBar != nil { hideSearch() } } func isKeyboardClosedManually() -> Bool { return keyboardFrameChangeCount > 2 } func addToolBar(textField: UITextView, toolbar: UIToolbar) { let scroll = UIScrollView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50)) scroll.showsHorizontalScrollIndicator = false scroll.contentSize = CGSize(width: toolbar.frame.width, height: 50) scroll.addSubview(toolbar) toolbar.frame.origin = .zero textField.inputAccessoryView = scroll } public func getMarkdownToolbar() -> UIToolbar { if #available(iOS 26.0, *) { return getModernToolbar() } else { return getLegacyToolbar() } } public func getModernToolbar() -> UIToolbar { var items = [UIBarButtonItem]() let todoButton = UIBarButtonItem(systemImageName: "checkmark.square", target: self, selector: #selector(EditorViewController.todoPressed)) items.append(todoButton) if UserDefaultsManagement.inlineTags { let tagButton = UIBarButtonItem(systemImageName: "tag", target: self, selector: #selector(EditorViewController.tagPressed)) items.append(tagButton) } let boldButton = UIBarButtonItem(systemImageName: "bold", target: self, selector: #selector(EditorViewController.boldPressed)) items.append(boldButton) let italicButton = UIBarButtonItem(systemImageName: "italic", target: self, selector: #selector(EditorViewController.italicPressed)) italicButton.tag = 0x03 items.append(italicButton) let headerButton = UIBarButtonItem(systemImageName: "textformat", target: self, selector: #selector(EditorViewController.headerPressed)) items.append(headerButton) let wikiButton = UIBarButtonItem(systemImageName: "link", target: self, selector: #selector(EditorViewController.wikilink)) items.append(wikiButton) let imageButton = UIBarButtonItem(systemImageName: "paperclip", target: self, selector: #selector(EditorViewController.insertFile)) items.append(imageButton) let codeblockButton = UIBarButtonItem(systemImageName: "swift", target: self, selector: #selector(EditorViewController.codeBlockButton)) items.append(codeblockButton) let quoteButton = UIBarButtonItem(systemImageName: "quote.bubble", target: self, selector: #selector(EditorViewController.quotePressed)) items.append(quoteButton) let orderedListButton = UIBarButtonItem(systemImageName: "list.bullet", target: self, selector: #selector(EditorViewController.orderedListPressed)) items.append(orderedListButton) let numberedListButton = UIBarButtonItem(systemImageName: "list.number", target: self, selector: #selector(EditorViewController.numberedListPressed)) items.append(numberedListButton) let indentButton = UIBarButtonItem(systemImageName: "increase.indent", target: self, selector: #selector(EditorViewController.indentPressed)) items.append(indentButton) let unindentButton = UIBarButtonItem(systemImageName: "decrease.indent", target: self, selector: #selector(EditorViewController.unIndentPressed)) items.append(unindentButton) self.undoBarButton = UIBarButtonItem(systemImageName: "arrow.uturn.backward", target: self, selector: #selector(EditorViewController.undoPressed)) items.append(self.undoBarButton!) self.redoBarButton = UIBarButtonItem(systemImageName: "arrow.uturn.forward", target: self, selector: #selector(EditorViewController.redoPressed)) items.append(self.redoBarButton!) var totalWidth: CGFloat = 0 for item in items { if item.tag == 0x03 { item.width = 30 totalWidth += 30 } else { item.width = 54 totalWidth += 54 } } let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: totalWidth, height: 50)) toolBar.setItems(items, animated: false) toolBar.isUserInteractionEnabled = true let appearance = UIToolbarAppearance() appearance.configureWithOpaqueBackground() appearance.backgroundColor = .darkGray appearance.shadowColor = .clear toolBar.standardAppearance = appearance toolBar.scrollEdgeAppearance = appearance return toolBar } private func getLegacyToolbar() -> UIToolbar { var items = [UIBarButtonItem]() let todoImage = UIImage(named: "toolbarTodo")?.resize(maxWidthHeight: 27) let todoButton = UIBarButtonItem(image: todoImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.todoPressed)) items.append(todoButton) if UserDefaultsManagement.inlineTags { let tagImage = UIImage(named: "toolbarTag")?.resize(maxWidthHeight: 25) let tagButton = UIBarButtonItem(image: tagImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.tagPressed)) items.append(tagButton) } let boldImage = UIImage(named: "toolbarBold")?.resize(maxWidthHeight: 21) let boldButton = UIBarButtonItem(image: boldImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.boldPressed)) items.append(boldButton) let italicImage = UIImage(named: "toolbarItalic")?.resize(maxWidthHeight: 18) let italicButton = UIBarButtonItem(image: italicImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.italicPressed)) italicButton.tag = 0x03 items.append(italicButton) let headerImage = UIImage(named: "toolbarHeader")?.resize(maxWidthHeight: 22) let headerButton = UIBarButtonItem(image: headerImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.headerPressed)) items.append(headerButton) let wikiImage = UIImage(named: "toolbarWiki")?.resize(maxWidthHeight: 25) let wikiButton = UIBarButtonItem(image: wikiImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.wikilink)) items.append(wikiButton) let toolbarImage = UIImage(named: "toolbarImage")?.resize(maxWidthHeight: 26) let imageButton = UIBarButtonItem(image: toolbarImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.insertFile)) items.append(imageButton) let codeBlockImage = UIImage(named: "codeBlockAsset")?.resize(maxWidthHeight: 24) let codeblockButton = UIBarButtonItem(image: codeBlockImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.codeBlockButton)) items.append(codeblockButton) let quoteImage = UIImage(named: "quote")?.resize(maxWidthHeight: 21) let quoteButton = UIBarButtonItem(image: quoteImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.quotePressed)) items.append(quoteButton) let orderedListImage = UIImage(named: "ordered_list")?.resize(maxWidthHeight: 25) let orderedListButton = UIBarButtonItem(image: orderedListImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.orderedListPressed)) items.append(orderedListButton) let numberedListImage = UIImage(named: "numbered_list")?.resize(maxWidthHeight: 25) let numberedListButton = UIBarButtonItem(image: numberedListImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.numberedListPressed)) items.append(numberedListButton) let indentRightImage = UIImage(named: "toolbarIndentRight")?.resize(maxWidthHeight: 25) let indentButton = UIBarButtonItem(image: indentRightImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.indentPressed)) items.append(indentButton) let indentLeftImage = UIImage(named: "toolbarIndentLeft")?.resize(maxWidthHeight: 25) let unindentButton = UIBarButtonItem(image: indentLeftImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.unIndentPressed)) items.append(unindentButton) let undoImage = UIImage(named: "undo")?.resize(maxWidthHeight: 25) let undoButton = UIBarButtonItem(image: undoImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.undoPressed)) items.append(undoButton) let redoImage = UIImage(named: "redo")?.resize(maxWidthHeight: 25) let redoButton = UIBarButtonItem(image: redoImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(EditorViewController.redoPressed)) items.append(redoButton) var width = CGFloat(0) for item in items { if item.tag == 0x03 { item.width = 30 width += 30 } else { item.width = 50 width += 50 } } let toolBar = UIToolbar(frame: CGRect.init(x: 0, y: 0, width: width, height: 50)) toolBar.backgroundColor = .darkGray toolBar.isTranslucent = false toolBar.tintColor = UIColor.mainTheme toolBar.setItems(items, animated: false) toolBar.isUserInteractionEnabled = true return toolBar } @objc func boldPressed(){ if let note = note { let formatter = TextFormatter(textView: editArea, note: note) formatter.bold() } } @objc func tagPressed(){ editArea.insertText("#") let location = editArea.selectedRange.location let vc = UIApplication.getVC() var projects = [Project]() if let project = Storage.shared().searchQuery.projects.first { projects.append(project) } else { projects = Storage.shared().getProjects() } let tags = vc.sidebarTableView.getAllTags(projects: projects) self.dropDown.dataSource = tags self.complete(offset: location) } @objc func italicPressed(){ if let note = note { let formatter = TextFormatter(textView: editArea, note: note) formatter.italic() } } @objc func strikePressed(){ if let note = note { let formatter = TextFormatter(textView: editArea, note: note) formatter.strike() } } @objc func underlinePressed(){ if let note = note { let formatter = TextFormatter(textView: editArea, note: note) formatter.underline() } } @objc func indentPressed(){ if let note = note { let formatter = TextFormatter(textView: editArea, note: note) formatter.tab() } } @objc func unIndentPressed(){ if let note = note { let formatter = TextFormatter(textView: editArea, note: note) formatter.unTab() } } @objc func headerPressed() { if let note = note { let formatter = TextFormatter(textView: editArea, note: note) formatter.header("#") } } @objc func wikilink() { guard let note = note else { return } let formatter = TextFormatter(textView: editArea, note: note) formatter.wikiLink() guard let titles = Storage.shared().getTitles() else { return } self.dropDown.dataSource = titles self.complete(offset: editArea.selectedRange.location, replacementRange: editArea.selectedRange) } @objc func codeBlockButton() { if let note = note { let formatter = TextFormatter(textView: editArea, note: note) formatter.codeBlock() } } @objc func quotePressed() { if let note = note { let formatter = TextFormatter(textView: editArea, note: note) formatter.quote() AudioServicesPlaySystemSound(1519) } } @objc func todoPressed() { if let note = note { let formatter = TextFormatter(textView: editArea, note: note) formatter.todo() AudioServicesPlaySystemSound(1519) } } @objc func orderedListPressed() { if let note = note { let formatter = TextFormatter(textView: editArea, note: note) formatter.list() AudioServicesPlaySystemSound(1519) } } @objc func numberedListPressed() { if let note = note { let formatter = TextFormatter(textView: editArea, note: note) formatter.orderedList() AudioServicesPlaySystemSound(1519) } } @objc func insertFile() { let actionSheet = UIAlertController(title: NSLocalizedString("Images source:", comment: ""), message: nil, preferredStyle: .actionSheet) let photos = UIAlertAction(title: NSLocalizedString("Photos", comment: ""), style: .default, handler: { _ in var conf = PHPickerConfiguration(photoLibrary: .shared()) conf.selectionLimit = 10 let picker = PHPickerViewController(configuration: conf) picker.delegate = self self.present(picker, animated: true, completion: nil) }) actionSheet.addAction(photos) let iCloudDrive = UIAlertAction(title: NSLocalizedString("Documents", comment: ""), style: .default, handler: { _ in let documentPickerController = UIDocumentPickerViewController(forOpeningContentTypes: [.data], asCopy: true) documentPickerController.delegate = self documentPickerController.allowsMultipleSelection = true documentPickerController.modalPresentationStyle = .formSheet self.present(documentPickerController, animated: true, completion: nil) }) actionSheet.addAction(iCloudDrive) let cancel = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .destructive, handler: { _ in }) actionSheet.addAction(cancel) if let view = self.editArea.superview { actionSheet.popoverPresentationController?.sourceView = view actionSheet.popoverPresentationController?.sourceRect = CGRect(x: view.bounds.size.width / 2.0, y: view.bounds.size.height, width: 2.0, height: 1.0) } present(actionSheet, animated: true, completion: nil) } func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { for url in urls { guard let data = try? Data(contentsOf: url), let mutable = NSMutableAttributedString.build(data: data, preferredName: url.lastPathComponent) else { continue } DispatchQueue.main.async { self.editArea.insertAttributedText(mutable) } } } @objc func themeObserver() { guard UIApplication.shared.applicationState == .active, let style = UIApplication.shared .connectedScenes .compactMap({ $0 as? UIWindowScene }) .first? .traitCollection .userInterfaceStyle else { return } guard style != lastStyle else { return } lastStyle = style NotesTextProcessor.hl = nil UIApplication.getEVC().refill() } @objc func preferredContentSizeChanged() { if let n = note { self.fill(note: n) } } @objc func undoPressed() { guard let ea = UIApplication.getEVC().editArea, let um = ea.undoManager else { return } um.undo() ea.initUndoRedoButons() } @objc func redoPressed() { guard let ea = UIApplication.getEVC().editArea, let um = ea.undoManager else { return } um.redo() ea.initUndoRedoButons() } func initLinksColor() { var linkAttributes: [NSAttributedString.Key : Any] = [ .foregroundColor: UIColor.linksColor ] linkAttributes[.underlineColor] = UIColor.lightGray linkAttributes[.underlineStyle] = 0 if editArea != nil { editArea.linkTextAttributes = linkAttributes } } @objc private func tapHandler(_ sender: SingleTouchDownGestureRecognizer) { let myTextView = sender.view as! EditTextView guard let characterIndex = sender.touchCharIndex else { return} let char = myTextView.textStorage.mutableString.substring(with: NSRange(location: characterIndex, length: 1)) // Toggle todo on click if characterIndex + 1 < myTextView.textStorage.length, char != "\n", self.isTodo(location: characterIndex, textView: myTextView), let note = self.note { let textFormatter = TextFormatter(textView: self.editArea!, note: note) textFormatter.toggleTodo(characterIndex) self.editArea.selectedTextRange = sender.selectedRange Timer.scheduledTimer(withTimeInterval: 0.05, repeats: false) { _ in self.editArea.isAllowedScrollRect = true } AudioServicesPlaySystemSound(1519) return } // Image preview/selection on click if self.editArea.isImage(at: characterIndex) { guard let meta = myTextView.textStorage.getMeta(at: characterIndex) else { return } if let data = try? Data(contentsOf: meta.url), let someImage = UIImage(data: data) { let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil) let imagePreviewViewController = storyBoard.instantiateViewController(withIdentifier: "imagePreviewViewController") as! ImagePreviewViewController imagePreviewViewController.image = someImage imagePreviewViewController.url = meta.url imagePreviewViewController.note = note navigationController?.pushViewController(imagePreviewViewController, animated: true) } else if (FileManager.default.fileExists(atPath: meta.url.path)) { quickLook(url: meta.url) } return } // Links if self.editArea.isLink(at: characterIndex) { guard let path = self.editArea.textStorage.attribute(.link, at: characterIndex, effectiveRange: nil) as? String else { return } if path.starts(with: "fsnotes://find?id=") { openWikiLink(query: path) return } if path.starts(with: "fsnotes://open/?tag=") { if let url = URL(string: path) { UIApplication.shared.open(url, options: [:]) } return } if self.editArea.isFirstResponder { DispatchQueue.main.async { self.editArea.selectedRange = NSRange(location: characterIndex, length: 0) } return } if let url = URL(string: path) { UIApplication.shared.open(url, options: [:], completionHandler: nil) } } } public func openWikiLink(query: String) { guard let query = query .replacingOccurrences(of: "fsnotes://find?id=", with: "") .removingPercentEncoding else { return } guard let note = note else { return } if let note = Storage.instance?.getBy(title: query, exclude: note) { fill(note: note) } else if let note = Storage.instance?.getBy(fileName: query, exclude: note) { fill(note: note) } else { let vc = UIApplication.getVC() navigationController?.popViewController(animated: true) DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { vc.shouldReturnToControllerIndex = true vc.loadSearchController(query: query) vc.buildSearchQuery() vc.reloadNotesTable() } } } @objc private func imageTapHandler(_ sender: SingleImageTouchDownGestureRecognizer) { guard let view = sender.view as? UITextView else { return } let layoutManager = view.layoutManager let location = sender.location(in: view) var characterIndex = layoutManager.characterIndex(for: location, in: view.textContainer, fractionOfDistanceBetweenInsertionPoints: nil) DispatchQueue.main.async { view.becomeFirstResponder() if sender.isRightBorderTap { characterIndex += 1 } view.selectedRange = NSRange(location: characterIndex, length: 0) } } private func isTodo(location: Int, textView: UITextView) -> Bool { let storage = textView.textStorage if storage.attribute(.todo, at: location, effectiveRange: nil) != nil { return true } let range = (storage.string as NSString).paragraphRange(for: NSRange(location: location, length: 0)) let string = storage.attributedSubstring(from: range).string as NSString var length = string.range(of: "- [ ]").length if length == 0 { length = string.range(of: "- [x]").length } if length > 0 { let upper = range.location + length if location >= range.location && location <= upper { return true } } return false } public func getMoreButton() -> UIBarButtonItem { let config = UIImage.SymbolConfiguration(pointSize: 23, weight: .light, scale: .default) let image = UIImage(systemName: "ellipsis.circle", withConfiguration: config) let menuBarItem = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(clickOnButton)) menuBarItem.tintColor = UIColor.mainTheme return menuBarItem } @IBAction func editMode() { if editArea.note?.previewState == true { togglePreview() _ = editArea.becomeFirstResponder() } } public func getPreviewView() -> MPreviewView? { for sub in self.view.subviews { if sub.isKind(of: MPreviewView.self) { if let view = sub as? MPreviewView { return view } } } return nil } @objc public func cancel() { navigationController?.popViewController(animated: true) } @objc public func togglePreview() { guard let unwrappedNote = self.note, let note = Storage.shared().getBy(url: unwrappedNote.url) else { return } note.loadPreviewState() if note.previewState { note.previewState = false getPreviewView()?.removeFromSuperview() fillEditor(note: note) } else { note.previewState = true editArea.endEditing(true) loadPreviewView() } let buttonName = note.previewState ? "eye.slash" : "eye" if let buttonBar = navigationItem.rightBarButtonItems?.first(where: { $0.tag == 5 }), let button = buttonBar.customView as? UIButton { let config = UIImage.SymbolConfiguration(pointSize: 20, weight: .light, scale: .default) let image = UIImage(systemName: buttonName, withConfiguration: config)?.imageWithColor(color1: UIColor.mainTheme) button.setImage(image, for: .normal) } // Handoff needs update in cursor position changed userActivity?.needsSave = true note.project.saveNotesPreview() } public func loadPreviewView() { guard let note = editArea.note else { return } var previewView: MPreviewView? previewView = getPreviewView() if previewView == nil { let newView = MPreviewView(frame: self.view.frame, note: note, closure: {}) newView.backgroundColor = UIColor.dropDownColor view.addSubview(newView) newView.translatesAutoresizingMaskIntoConstraints = false view.leadingAnchor.constraint(equalTo: newView.leadingAnchor).isActive = true view.trailingAnchor.constraint(equalTo: newView.trailingAnchor).isActive = true view.topAnchor.constraint(equalTo: newView.topAnchor).isActive = true view.bottomAnchor.constraint(equalTo: newView.bottomAnchor).isActive = true } guard let previewView = previewView else { return } previewView.clean() previewView.load(note: note, force: true) } func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { if URL.absoluteString.starts(with: "fsnotes://find?id=") { if interaction == .invokeDefaultAction { openWikiLink(query: URL.absoluteString) } return false } if URL.absoluteString.starts(with: "fsnotes://open/?tag=") { if interaction == .invokeDefaultAction { UIApplication.shared.open(URL, options: [:]) } // if textView.isFirstResponder { // UIApplication.shared.open(URL, options: [:]) // } return false } if textView.isFirstResponder { DispatchQueue.main.async { textView.selectedRange = NSRange(location: characterRange.upperBound, length: 0) } if interaction == .presentActions { let pathKey = NSAttributedString.Key(rawValue: "co.fluder.fsnotes.image.path") if nil != textView.textStorage.attribute(pathKey, at: characterRange.location, effectiveRange: nil) { return false } return true } return false } // Skip images (fixes glitch bug) let pathKey = NSAttributedString.Key(rawValue: "co.fluder.fsnotes.image.path") let attr = textView.textStorage.attribute(pathKey, at: characterRange.location, effectiveRange: nil) if attr != nil && !textView.isFirstResponder { return false } return true } func textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { return false } func textViewDidEndEditing(_ textView: UITextView) { editArea.keyboardIsOpened = true editArea.callCounter = 0 } /* Handoff methods */ public func registerHandoff(for note: Note) { let updateDict: [String: String] = ["note-file-name": note.name] let activity = NSUserActivity(activityType: "es.fsnot.handoff-open-note") activity.isEligibleForHandoff = true activity.addUserInfoEntries(from: updateDict) activity.title = NSLocalizedString("Open Note", comment: "Document opened") self.userActivity = activity self.userActivity?.becomeCurrent() } public func load(note: Note) { let evc = UIApplication.getEVC() evc.editArea.resignFirstResponder() evc.fill(note: note, clearPreview: true, enableHandoff: false) { UIApplication.getVC().openEditorViewController() } } override func restoreUserActivityState(_ activity: NSUserActivity) { if let id = activity.userInfo?["kCSSearchableItemActivityIdentifier"] as? String { let url = URL(fileURLWithPath: id) var note = Storage.shared().getBy(url: url) if nil === note { note = Storage.shared().addNote(url: url) } if let note = note { load(note: note) } return } guard let name = activity.userInfo?["note-file-name"] as? String, let position = activity.userInfo?["position"] as? String, let note = Storage.shared().getBy(name: name) else { return } let evc = UIApplication.getEVC() evc.editArea.resignFirstResponder() evc.fill(note: note, clearPreview: true, enableHandoff: false) { UIApplication.getVC().openEditorViewController() if let pos = Int(position), pos > -1, evc.editArea.textStorage.length >= pos { evc.editArea.becomeFirstResponder() evc.editArea.selectedRange = NSRange(location: pos, length: 0) } } } override func updateUserActivityState(_ activity: NSUserActivity) { guard let note = editArea.note else { return } let position = editArea.isFirstResponder ? editArea.selectedRange.location : -1 let state = note.previewState ? "preview" : "editor" let data = [ "note-file-name": note.name, "position": String(position), "state": state ] activity.addUserInfoEntries(from: data) } @available(iOS 14, *) func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { picker.dismiss(animated: true, completion: nil) for result in results { result.itemProvider.loadObject(ofClass: UIImage.self, completionHandler: { (object, error) in guard let photo = object as? UIImage, let imageData = photo.jpgData else { return } let attachment = NSTextAttachment() let mutable = NSMutableAttributedString(attachment: attachment) mutable.addAttributes([ .attachmentSave: imageData, .attachmentUrl: URL(fileURLWithPath: "/tmp/" + UUID().uuidString + ".jpg"), .attachmentPath: String() ], range: NSRange(location: 0, length: 1)) mutable.append(NSAttributedString(string: "\n\n")) DispatchQueue.main.async { self.editArea.insertAttributedText(mutable) } }) } } // Swipe controller from UITextView center // https://stackoverflow.com/questions/22244688/navigation-pop-view-when-swipe-right-like-instagram-iphone-app-how-i-achieve-thi/22244990#22244990 public func initSwipes() { guard let popGestureRecognizer = self.navigationController?.interactivePopGestureRecognizer else { return } if let targets = popGestureRecognizer.value(forKey: "targets") as? NSMutableArray { let gestureRecognizer = UIPanGestureRecognizer() gestureRecognizer.setValue(targets, forKey: "targets") self.view.gestureRecognizers?.removeAll() self.view.addGestureRecognizer(gestureRecognizer) let tapGR = UITapGestureRecognizer(target: self, action: #selector(editMode)) tapGR.delegate = self tapGR.numberOfTapsRequired = 2 self.view.addGestureRecognizer(tapGR) } } func saveSelectedRangeZero() { guard let note = editArea.note, !editArea.isNoteLoading else { return } note.setSelectedRange(range: nil) saveScrollPosition() } func saveScrollPosition() { guard !editArea.isUpdating else { return } guard let note = editArea.note, !editArea.isNoteLoading else { return } let layoutManager = editArea.layoutManager let textStorage = editArea.textStorage let visibleY = editArea.contentOffset.y guard textStorage.length > 0 else { note.scrollPosition = 0 note.scrollOffset = 0 return } let glyphRange = layoutManager.glyphRange( forBoundingRect: CGRect(x: 0, y: visibleY, width: editArea.bounds.width, height: 1), in: editArea.textContainer ) guard glyphRange.location != NSNotFound else { note.scrollPosition = 0 note.scrollOffset = 0 return } let numberOfGlyphs = layoutManager.numberOfGlyphs guard glyphRange.location < numberOfGlyphs else { note.scrollPosition = 0 note.scrollOffset = 0 return } let charIndex = layoutManager.characterIndexForGlyph(at: glyphRange.location) guard charIndex < textStorage.length else { note.scrollPosition = 0 note.scrollOffset = 0 return } let glyphRect = layoutManager.boundingRect( forGlyphRange: NSRange(location: glyphRange.location, length: 1), in: editArea.textContainer ) note.scrollPosition = charIndex note.scrollOffset = visibleY - glyphRect.minY } func saveSelectedRange() { guard let note = editArea.note, !editArea.isNoteLoading else { return } note.setSelectedRange(range: editArea.isFirstResponder ? editArea.selectedRange : nil) saveScrollPosition() } func loadSelectedRange() { guard let note = editArea.note else { return } if let range = note.getSelectedRange(), range.upperBound <= editArea.textStorage.length { editArea.selectedRange = range _ = editArea.becomeFirstResponder() } DispatchQueue.main.async { [weak self] in guard let self = self else { return } if let index = note.scrollPosition, index < self.editArea.textStorage.length { let layoutManager = self.editArea.layoutManager let glyphIndex = layoutManager.glyphIndexForCharacter(at: index) let glyphRect = layoutManager.boundingRect( forGlyphRange: NSRange(location: glyphIndex, length: 1), in: self.editArea.textContainer ) let targetY = glyphRect.minY + (note.scrollOffset ?? 0) let maxY = max(0, self.editArea.contentSize.height - self.editArea.bounds.height) let finalY = min(max(0, targetY), maxY) self.editArea.contentOffset = CGPoint(x: 0, y: finalY) } else { self.editArea.contentOffset = .zero } self.editArea.isNoteLoading = false } } deinit { NotificationCenter.default.removeObserver(self) } } ================================================ FILE: FSNotes iOS/Extensions/UIApplication+.swift ================================================ // // UIApplication.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 9/21/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit extension UIApplication { // MARK: - Scene Delegate Access static func getSceneDelegate() -> SceneDelegate? { guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let sceneDelegate = windowScene.delegate as? SceneDelegate else { return nil } return sceneDelegate } // MARK: - View Controllers Access static func getVC() -> ViewController { guard let sceneDelegate = getSceneDelegate() else { fatalError("SceneDelegate not found") } return sceneDelegate.listController } static func getEVC() -> EditorViewController { guard let sceneDelegate = getSceneDelegate() else { fatalError("SceneDelegate not found") } return sceneDelegate.editorController } static func getNC() -> MainNavigationController? { guard let sceneDelegate = getSceneDelegate() else { return nil } return sceneDelegate.mainController } // MARK: - App Delegate Access static func getDelegate() -> AppDelegate { return UIApplication.shared.delegate as! AppDelegate } // MARK: - Window Access (Optional Helper) static func getWindow() -> UIWindow? { return getSceneDelegate()?.window } } ================================================ FILE: FSNotes iOS/Extensions/UIBarButtonItem+.swift ================================================ // // UIBarButton+.swift // FSNotes // // Created by Oleksandr Hlushchenko on 15.11.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import UIKit extension UIBarButtonItem { convenience init(systemImageName: String, target: Any, selector: Selector) { let config = UIImage.SymbolConfiguration(pointSize: 20, weight: .light, scale: .default) let image = UIImage(systemName: systemImageName, withConfiguration: config)?.imageWithColor(color1: UIColor.mainTheme) let button = UIButton(type: .system) button.setImage(image, for: .normal) button.tintColor = .mainTheme button.addTarget(target, action: selector, for: .touchUpInside) self.init(customView: button) } convenience init(systemImageName: String, menu: UIMenu) { let config = UIImage.SymbolConfiguration(pointSize: 20, weight: .light) let image = UIImage(systemName: systemImageName, withConfiguration: config)? .imageWithColor(color1: .mainTheme) let button = UIButton(type: .system) button.setImage(image, for: .normal) button.tintColor = .mainTheme button.menu = menu button.showsMenuAsPrimaryAction = true button.frame = CGRect(x: 0, y: 0, width: 30, height: 30) self.init(customView: button) } } ================================================ FILE: FSNotes iOS/Extensions/UIColor+.swift ================================================ // // UIColor+.swift // FSNotes // // Created by Oleksandr Hlushchenko on 12.05.2023. // Copyright © 2023 Oleksandr Hlushchenko. All rights reserved. // import UIKit extension UIColor { public static let mainTheme = UIColor(red: 0.08, green: 0.60, blue: 0.85, alpha: 1.00) public static var previewColor: UIColor { return UIColor { (traits) -> UIColor in return traits.userInterfaceStyle == .dark ? UIColor(red: 0.85, green: 0.87, blue: 0.90, alpha: 1.00) : UIColor(red: 0.50, green: 0.56, blue: 0.65, alpha: 1.00) } } public static var sidebar: UIColor { return UIColor { (traits) -> UIColor in return traits.userInterfaceStyle == .dark ? UIColor(red: 0.11, green: 0.11, blue: 0.11, alpha: 1.00) : UIColor(red: 0.97, green: 0.97, blue: 0.97, alpha: 1.00) } } public static var currentSidebarCell: UIColor { return UIColor { (traits) -> UIColor in return traits.userInterfaceStyle == .dark ? UIColor(red: 0.41, green: 0.39, blue: 0.45, alpha: 1.00) : UIColor(red: 0.81, green: 0.87, blue: 0.95, alpha: 1.00) } } public static var codeBackground: UIColor { return UIColor { (traits) -> UIColor in return traits.userInterfaceStyle == .dark ? UIColor(red: 0.27, green: 0.27, blue: 0.27, alpha: 1.00) : UIColor(red: 0.94, green: 0.95, blue: 0.95, alpha: 1.00) } } public static var whiteBlack: UIColor { return UIColor { (traits) -> UIColor in return traits.userInterfaceStyle == .dark ? UIColor(red: 0.00, green: 0.00, blue: 0.00, alpha: 1.00) : UIColor(red: 1.00, green: 1.00, blue: 1.00, alpha: 1.00) } } // Black for normal, white for dark public static var blackWhite: UIColor { return UIColor { (traits) -> UIColor in return traits.userInterfaceStyle == .dark ? UIColor(red: 1.00, green: 1.00, blue: 1.00, alpha: 1.00) : UIColor(red: 0.00, green: 0.00, blue: 0.00, alpha: 1.00) } } public static var toolbarTint: UIColor { return UIColor { (traits) -> UIColor in return traits.userInterfaceStyle == .dark ? UIColor(red: 0.49, green: 0.92, blue: 0.63, alpha: 1.00) : UIColor(red: 0.30, green: 0.55, blue: 0.90, alpha: 1.00) } } public static var toolbarBorder: UIColor { return UIColor { (traits) -> UIColor in return traits.userInterfaceStyle == .dark ? UIColor(red: 0.22, green: 0.22, blue: 0.22, alpha: 1.00) : UIColor(red: 0.84, green: 0.84, blue: 0.87, alpha: 1.00) } } public static var dropDownColor: UIColor { return UIColor { (traits) -> UIColor in return traits.userInterfaceStyle == .dark ? UIColor(red: 0.00, green: 0.00, blue: 0.00, alpha: 1.00) : UIColor(red: 0.98, green: 0.98, blue: 0.98, alpha: 1.00) } } public static var wikiColor: UIColor { return UIColor { (traits) -> UIColor in return traits.userInterfaceStyle == .dark ? UIColor(red: 0.00, green: 0.45, blue: 0.15, alpha: 1.00) : UIColor(red: 0.29, green: 0.35, blue: 0.60, alpha: 1.00) } } public static var highlightColor: UIColor { return UIColor { (traits) -> UIColor in return traits.userInterfaceStyle == .dark ? UIColor(red: 0.20, green: 0.55, blue: 0.07, alpha: 1.00) : UIColor(red: 1.00, green: 0.90, blue: 0.70, alpha: 1.00) } } public static var linksColor: UIColor { return UIColor { (traits) -> UIColor in return traits.userInterfaceStyle == .dark ? UIColor(red: 0.08, green: 0.60, blue: 0.85, alpha: 1.00) : UIColor(red: 0.08, green: 0.60, blue: 0.85, alpha: 1.00) } } public static func getBy(hex: String) -> UIColor { var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() if (cString.hasPrefix("#")) { cString.remove(at: cString.startIndex) } if ((cString.count) != 6) { return UIColor.gray } var rgbValue:UInt64 = 0 Scanner(string: cString).scanHexInt64(&rgbValue) return UIColor( red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, blue: CGFloat(rgbValue & 0x0000FF) / 255.0, alpha: CGFloat(1.0) ) } var hexString: String { var red: CGFloat = 0 var green: CGFloat = 0 var blue: CGFloat = 0 var alpha: CGFloat = 0 guard self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) else { return "#ffffff" } let rgb: Int = (Int)(red * 255) << 16 | (Int)(green * 255) << 8 | (Int)(blue * 255) << 0 return String(format: "#%06x", rgb) } } ================================================ FILE: FSNotes iOS/Extensions/UIFont+.swift ================================================ // // UIFont+.swift // FSNotes // // Created by Oleksandr Glushchenko on 3/6/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit extension UIFont { var isItalic: Bool { return fontDescriptor.symbolicTraits.contains(.traitItalic) } func bold() -> UIFont { if let descriptor = fontDescriptor.withSymbolicTraits(.traitBold) { return UIFont(descriptor: descriptor, size: 0) } return UserDefaultsManagement.noteFont } private func buildFont(symTraits: UIFontDescriptor.SymbolicTraits?) -> UIFont { var font: UIFont if let traits = symTraits, let descriptor = fontDescriptor.withSymbolicTraits(traits) { font = UIFont(descriptor: descriptor, size: descriptor.pointSize) } else { font = UserDefaultsManagement.noteFont font.withSize(fontDescriptor.pointSize) return font } return font } public func getAttachmentHeight() -> Double { return Double(pointSize) + 6 } } ================================================ FILE: FSNotes iOS/Extensions/UIImage+.swift ================================================ // // UIImage+.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 6/5/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit extension UIImage { func alpha(_ value:CGFloat) -> UIImage { UIGraphicsBeginImageContextWithOptions(size, false, scale) draw(at: CGPoint.zero, blendMode: .normal, alpha: value) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! } func resize(maxWidthHeight : Double)-> UIImage? { let actualHeight = Double(size.height) let actualWidth = Double(size.width) var maxWidth = 0.0 var maxHeight = 0.0 if actualWidth > actualHeight { maxWidth = maxWidthHeight let per = (100.0 * maxWidthHeight / actualWidth) maxHeight = (actualHeight * per) / 100.0 }else{ maxHeight = maxWidthHeight let per = (100.0 * maxWidthHeight / actualHeight) maxWidth = (actualWidth * per) / 100.0 } let hasAlpha = true let scale: CGFloat = 0.0 UIGraphicsBeginImageContextWithOptions(CGSize(width: maxWidth, height: maxHeight), !hasAlpha, scale) self.draw(in: CGRect(origin: .zero, size: CGSize(width: maxWidth, height: maxHeight))) let scaledImage = UIGraphicsGetImageFromCurrentImageContext() return scaledImage } func resize(height : Double)-> UIImage? { let actualHeight = Double(size.height) let actualWidth = Double(size.width) var maxWidth = 0.0 var maxHeight = 0.0 var per: Double = 0 if actualWidth < actualHeight { per = (70 / actualWidth) maxWidth = (actualWidth * per) maxHeight = (actualHeight * per) } else{ per = (70 / actualHeight) maxWidth = (actualWidth * per) maxHeight = (actualHeight * per) } let newSize = CGSize(width: maxWidth, height: maxHeight) let renderer = UIGraphicsImageRenderer(size: newSize) let image = renderer.image { (context) in self.draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: newSize)) } return image } func croppedInRect(rect: CGRect) -> UIImage { func rad(_ degree: Double) -> CGFloat { return CGFloat(degree / 180.0 * .pi) } var rectTransform: CGAffineTransform switch imageOrientation { case .left: rectTransform = CGAffineTransform(rotationAngle: rad(90)).translatedBy(x: 0, y: -self.size.height) case .right: rectTransform = CGAffineTransform(rotationAngle: rad(-90)).translatedBy(x: -self.size.width, y: 0) case .down: rectTransform = CGAffineTransform(rotationAngle: rad(-180)).translatedBy(x: -self.size.width, y: -self.size.height) default: rectTransform = .identity } rectTransform = rectTransform.scaledBy(x: self.scale, y: self.scale) let imageRef = self.cgImage!.cropping(to: rect.applying(rectTransform)) let result = UIImage(cgImage: imageRef!, scale: self.scale, orientation: self.imageOrientation) return result } public func getScale() -> Int { let actualHeight = Double(size.height) let actualWidth = Double(size.width) if actualWidth < actualHeight { return Int(70 / actualWidth) } else{ return Int(70 / actualHeight) } } public var jpgData: Data? { return self.jpegData(compressionQuality: 1) } public static func emptyImage(with size: CGSize) -> UIImage? { let renderer = UIGraphicsImageRenderer(size: size) let img = renderer.image { ctx in let border = UIColor.blackWhite ctx.cgContext.setStrokeColor(border.cgColor) ctx.cgContext.setLineWidth(1) let rectangle = CGRect(x: 0, y: 0, width: size.width, height: size.height) ctx.cgContext.addRect(rectangle) ctx.cgContext.drawPath(using: .stroke) } return img } public func rounded(radius: CGFloat) -> UIImage { let rect = CGRect(origin: .zero, size: size) UIGraphicsBeginImageContextWithOptions(size, false, 0) UIBezierPath(roundedRect: rect, cornerRadius: radius).addClip() draw(in: rect) return UIGraphicsGetImageFromCurrentImageContext()! } public func imageWithColor(color1: UIColor) -> UIImage { UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale) color1.setFill() let context = UIGraphicsGetCurrentContext() context?.translateBy(x: 0, y: self.size.height) context?.scaleBy(x: 1.0, y: -1.0) context?.setBlendMode(CGBlendMode.normal) let rect = CGRect(origin: .zero, size: CGSize(width: self.size.width, height: self.size.height)) context?.clip(to: rect, mask: self.cgImage!) context?.fill(rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! } func resized(to newSize: CGSize) -> UIImage? { UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0) defer { UIGraphicsEndImageContext() } draw(in: CGRect(origin: .zero, size: newSize)) return UIGraphicsGetImageFromCurrentImageContext() } } ================================================ FILE: FSNotes iOS/Extensions/UITextView+.swift ================================================ // // UITextView+.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 7/20/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit extension UITextView { var cursorOffset: Int? { guard let range = selectedTextRange else { return nil } return offset(from: beginningOfDocument, to: range.start) } var cursorIndex: String.Index? { guard let location = cursorOffset, case let length = text.utf16.count-location else { return nil } return Range(.init(location: location, length: length), in: text)?.lowerBound } var cursorDistance: Int? { guard let cursorIndex = cursorIndex else { return nil } return text.distance(from: text.startIndex, to: cursorIndex) } public func getTextRange() -> UITextRange? { if let start = position(from: self.beginningOfDocument, offset: self.selectedRange.location), let end = position(from: start, offset: self.selectedRange.length), let selectedRange = textRange(from: start, to: end) { return selectedRange } return nil } public func insertAttributedText(_ attr: NSAttributedString) { let range = self.selectedRange undoManager?.beginUndoGrouping() let old = textStorage.attributedSubstring(from: range) let oldMutable = NSMutableAttributedString(attributedString: old) oldMutable.saveData() undoManager?.registerUndo(withTarget: self) { target in target.replace(range: NSRange(location: range.location, length: attr.length), with: oldMutable) } undoManager?.setActionName("Insert") textStorage.beginEditing() textStorage.replaceCharacters(in: range, with: attr) textStorage.endEditing() selectedRange = NSRange(location: range.location + attr.length, length: 0) undoManager?.endUndoGrouping() delegate?.textViewDidChange?(self) } private func replace(range: NSRange, with attr: NSAttributedString) { let old = textStorage.attributedSubstring(from: range) let oldMutable = NSMutableAttributedString(attributedString: old) oldMutable.saveData() undoManager?.registerUndo(withTarget: self) { target in target.replace(range: NSRange(location: range.location, length: attr.length), with: oldMutable) } textStorage.beginEditing() textStorage.replaceCharacters(in: range, with: attr) textStorage.endEditing() selectedRange = NSRange(location: range.location + attr.length, length: 0) delegate?.textViewDidChange?(self) } } ================================================ FILE: FSNotes iOS/Extensions/UserDefaultsManagement+.swift ================================================ // // UserDefaultsManagement+.swift // FSNotes // // Created by Oleksandr Glushchenko on 10/26/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation import UIKit extension UserDefaultsManagement { private struct Constants { static let appIcon = "appIcon2025" static let currentNote = "currentNote" static let currentLocation = "currentLocation" static let currentLength = "currentLength" static let dynamicTypeFont = "dynamicTypeFont" static let editorAutocorrection = "editorAutocorrection" static let editorState = "editorState" static let editorSuggestions = "editorSuggestions" static let ImportURLsKey = "ImportURLs" } static var appIcon: Int { get { if let theme = shared?.integer(forKey: Constants.appIcon) { return theme } return 1 } set { shared?.set(newValue, forKey: Constants.appIcon) } } static var dynamicTypeFont: Bool { get { if let result = shared?.object(forKey: Constants.dynamicTypeFont) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.dynamicTypeFont) } } static var sidebarIsOpened: Bool { get { if let result = shared?.object(forKey: "sidebarIsOpened") as? Bool { return result } return false } set { shared?.set(newValue, forKey: "sidebarIsOpened") } } static var editorAutocorrection: Bool { get { if let result = shared?.object(forKey: Constants.editorAutocorrection) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.editorAutocorrection) } } static var editorSpellChecking: Bool { get { if let result = shared?.object(forKey: Constants.editorSuggestions) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.editorSuggestions) } } static var currentNote: URL? { get { if let url = shared?.url(forKey: Constants.currentNote) { return url } return nil } set { shared?.set(newValue, forKey: Constants.currentNote) } } static var currentRange: NSRange? { get { if let location = shared?.integer(forKey: Constants.currentLocation), let length = shared?.integer(forKey: Constants.currentLength) { return NSRange(location: location, length: length) } return nil } set { if let range = newValue { shared?.set(range.location, forKey: Constants.currentLocation) shared?.set(range.length, forKey: Constants.currentLength) } else { shared?.set(nil, forKey: Constants.currentLocation) shared?.set(nil, forKey: Constants.currentLength) } } } static var currentEditorState: Bool? { get { if let result = shared?.object(forKey: Constants.editorState) as? Bool { return result } return nil } set { shared?.set(newValue, forKey: Constants.editorState) } } static var noteFont: UIFont { get { if #available(iOS 11.0, *), UserDefaultsManagement.dynamicTypeFont { var font = UIFont.systemFont(ofSize: CGFloat(DefaultFontSize)) if let fontName = UserDefaultsManagement.fontName, let currentFont = UIFont(name: fontName, size: CGFloat(DefaultFontSize)) { font = currentFont } let fontMetrics = UIFontMetrics(forTextStyle: .body) return fontMetrics.scaledFont(for: font) } if let name = self.fontName, name.starts(with: ".") { return UIFont.systemFont(ofSize: CGFloat(self.fontSize)) } if let fontName = self.fontName, let font = UIFont(name: fontName, size: CGFloat(self.fontSize)) { return font } return UIFont.systemFont(ofSize: CGFloat(self.fontSize)) } set { self.fontName = newValue.fontName self.fontSize = Int(newValue.pointSize) } } static var codeFont: UIFont { get { if #available(iOS 11.0, *), UserDefaultsManagement.dynamicTypeFont { var font = Font.systemFont(ofSize: CGFloat(self.codeFontSize)) if let currentFont = Font(name: self.codeFontName, size: CGFloat(self.codeFontSize)) { font = currentFont } let fontMetrics = UIFontMetrics(forTextStyle: .body) return fontMetrics.scaledFont(for: font) } if let font = UIFont(name: self.codeFontName, size: CGFloat(self.codeFontSize)) { return font } return UIFont.systemFont(ofSize: CGFloat(self.codeFontSize)) } set { self.codeFontName = newValue.familyName self.codeFontSize = Int(newValue.pointSize) } } @available(iOS 11.0, *) static var importURLs: [URL] { get { guard let defaults = UserDefaults.init(suiteName: "group.es.fsnot.user.defaults") else { return [] } if let result = defaults.object(forKey: Constants.ImportURLsKey) as? Data, let urls = NSArray.unsecureUnarchived(from: result) as? [URL] { return urls } return [] } set { guard let defaults = UserDefaults.init(suiteName: "group.es.fsnot.user.defaults") else { return } if let data = try? NSKeyedArchiver.archivedData(withRootObject: newValue, requiringSecureCoding: true) { defaults.set(data, forKey: Constants.ImportURLsKey) } } } static var fontColor: Color { get { return self.DefaultFontColor } } static var bgColor: Color { get { return self.DefaultBgColor } } } extension NSCoding where Self: NSObject { @available(iOS 11.0, *) static func unsecureUnarchived(from data: Data) -> Self? { do { let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data) unarchiver.requiresSecureCoding = true let obj = unarchiver.decodeObject(of: self, forKey: NSKeyedArchiveRootObjectKey) if let error = unarchiver.error { print("Error:\(error)") } return obj } catch { print("Error:\(error)") } return nil } } ================================================ FILE: FSNotes iOS/FSNotes iOS.entitlements ================================================ aps-environment development com.apple.developer.icloud-container-environment Production com.apple.developer.icloud-container-identifiers iCloud.co.fluder.fsnotes com.apple.developer.icloud-services CloudDocuments com.apple.developer.ubiquity-container-identifiers iCloud.co.fluder.fsnotes com.apple.developer.ubiquity-kvstore-identifier $(TeamIdentifierPrefix)co.fluder.fsnotes com.apple.security.application-groups group.es.fsnot.user.defaults ================================================ FILE: FSNotes iOS/FSNotes_iOS.xcdatamodeld/.xccurrentversion ================================================ ================================================ FILE: FSNotes iOS/FSNotes_iOS.xcdatamodeld/FSNotes_iOS.xcdatamodel/contents ================================================ ================================================ FILE: FSNotes iOS/Helpers/Buttons.swift ================================================ // // Buttons.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 9/20/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit class Buttons { public static func getRateUs(target: Any, selector: Selector) -> UIBarButtonItem { return UIBarButtonItem(systemImageName: "heart", target: target, selector: selector) } public static func getAdd(target: Any, selector: Selector) -> UIBarButtonItem { return UIBarButtonItem(systemImageName: "plus", target: target, selector: selector) } public static func getDone(target: Any, selector: Selector) -> UIBarButtonItem { return UIBarButtonItem(title: NSLocalizedString("Done", comment: ""), style: .plain, target: target, action: selector) } public static func getShare(target: Any, selector: Selector) -> UIBarButtonItem { return UIBarButtonItem(systemImageName: "square.and.arrow.up", target: target, selector: selector) } public static func getCrop(target: Any, selector: Selector) -> UIBarButtonItem { return UIBarButtonItem(systemImageName: "crop", target: target, selector: selector) } public static func getTrash(target: Any, selector: Selector) -> UIBarButtonItem { return UIBarButtonItem(systemImageName: "trash", target: target, selector: selector) } public static func getNewNote(target: Any, selector: Selector) -> UIBarButtonItem { let config = UIImage.SymbolConfiguration(pointSize: 20, weight: .bold, scale: .default) let image = UIImage(systemName: "square.and.pencil", withConfiguration: config)? .imageWithColor(color1: UIColor.mainTheme) let button = UIButton(type: .system) button.setImage(image, for: .normal) button.tintColor = .mainTheme button.imageEdgeInsets = UIEdgeInsets(top: -2, left: 3, bottom: 2, right: -3) button.addTarget(target, action: selector, for: .touchUpInside) return UIBarButtonItem(customView: button) } } ================================================ FILE: FSNotes iOS/Helpers/CloudDriveManager.swift ================================================ // // CloudDriveManager.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 6/13/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation import UIKit class CloudDriveManager { private var cloudDriveResults = [URL]() private var delegate: ViewController private var storage: Storage public let metadataQuery = NSMetadataQuery() private var resultsDict = NSMutableDictionary() private let workerQueue: OperationQueue = { let workerQueue = OperationQueue() workerQueue.name = "co.fluder.fsnotes.manager.browserdatasource.workerQueue" workerQueue.maxConcurrentOperationCount = 1 workerQueue.qualityOfService = .background return workerQueue }() private var shouldLoadTags: Bool = false private var notesInsertionQueue = [Note]() private var notesDeletionQueue = [Note]() private var notesModificationQueue = [Note]() private var projectsInsertionQueue = [Project]() private var projectsDeletionQueue = [Project]() init(delegate: ViewController, storage: Storage) { metadataQuery.operationQueue = workerQueue metadataQuery.searchScopes = [ NSMetadataQueryUbiquitousDocumentsScope ] metadataQuery.notificationBatchingInterval = 1 metadataQuery.predicate = NSPredicate(format: "%K LIKE '*'", NSMetadataItemFSNameKey) metadataQuery.sortDescriptors = [NSSortDescriptor(key: NSMetadataItemFSContentChangeDateKey, ascending: false)] self.delegate = delegate self.storage = storage } @objc func queryDidFinishGathering(notification: NSNotification) { let query = notification.object as? NSMetadataQuery if let results = query?.results as? [NSMetadataItem] { saveCloudDriveResultsCache(results: results) startInitialLoading(results: results) } metadataQuery.enableUpdates() } @objc func handleMetadataQueryUpdates(notification: NSNotification) { guard let metadataQuery = notification.object as? NSMetadataQuery else { return } metadataQuery.disableUpdates() let changed = change(notification: notification) let added = added(notification: notification) let removed = remove(notification: notification) doVisualChanges() if added > 0 || removed > 0 || changed > 0, let results = metadataQuery.results as? [NSMetadataItem] { saveCloudDriveResultsCache(results: results) } metadataQuery.enableUpdates() } private func saveCloudDriveResultsCache(results: [NSMetadataItem]) { // let point = Date() for result in results { if let url = result.value(forAttribute: NSMetadataItemURLKey) as? URL { resultsDict[metadataQuery.index(ofResult: result)] = url.standardized } } // print("N. iCloud Drive resources: \"\(results.count)\", caching finished in \(point.timeIntervalSinceNow * -1) seconds.") } private func startInitialLoading(results: [NSMetadataItem]) { for metadataItem in results { let url = metadataItem.value(forAttribute: NSMetadataItemURLKey) as? URL let status = metadataItem.value(forAttribute: NSMetadataUbiquitousItemDownloadingStatusKey) as? String if status == NSMetadataUbiquitousItemDownloadingStatusNotDownloaded, let url = url, FileManager.default.isUbiquitousItem(at: url) { do { try FileManager.default.startDownloadingUbiquitousItem(at: url) } catch { print("Download error: \(error)") } } } } private func isProject(item: NSMetadataItem) -> Bool { let itemUrl = item.value(forAttribute: NSMetadataItemURLKey) as? URL let isDirectory = (item.value(forAttribute: NSMetadataItemContentTypeKey) as? String) == "public.folder" let isPackage = (try? itemUrl?.resourceValues(forKeys: [.isDirectoryKey]))?.isPackage ?? false guard let url = itemUrl?.standardized else { return false } return isDirectory && !isPackage && url.pathExtension != "textbundle" } private func change(notification: NSNotification) -> Int { guard let changedMetadataItems = notification.userInfo?[NSMetadataQueryUpdateChangedItemsKey] as? [NSMetadataItem] else { return 0 } var completed = 0 for item in changedMetadataItems { let status = item.value(forAttribute: NSMetadataUbiquitousItemDownloadingStatusKey) as? String let index = metadataQuery.index(ofResult: item) let itemUrl = item.value(forAttribute: NSMetadataItemURLKey) as? URL let contentChangeDate = item.value(forAttribute: NSMetadataItemFSContentChangeDateKey) as? Date let creationDate = item.value(forAttribute: NSMetadataItemFSCreationDateKey) as? Date if status == NSMetadataUbiquitousItemDownloadingStatusCurrent { completed += 1 } guard let url = itemUrl?.standardized, status == NSMetadataUbiquitousItemDownloadingStatusCurrent else { continue } if isProject(item: item) { // Renamed – remove old if let project = getProjectFromCloudDriveResults(item: item) { // Remove old projectsDeletionQueue.append(project) // Insert new if let projects = storage.insert(url: url) { projectsInsertionQueue.append(contentsOf: projects) } } else { // Move from outside iCloud Drive if storage.getProjectBy(url: url) == nil { if let projects = storage.insert(url: url) { projectsInsertionQueue.append(contentsOf: projects) } } } continue } if url.lastPathComponent == ".encrypt" { self.loadEncryptionStatus(url: url) continue } // Is file guard storage.isValidNote(url: url) else { continue } // Note already exist and update completed if let note = storage.getBy(url: url, caseSensitive: true) { if note.isTextBundle() && !note.isFullLoadedTextBundle() { continue } let modificationDate = note.getFileModifiedDate() let isOpened = delegate.editorViewController?.editArea.note?.isEqualURL(url: url) == true if let modificationDate = modificationDate, let contentChangeDate = contentChangeDate, isOpened, modificationDate.isGreaterThan(note.modifiedLocalAt), contentChangeDate.isGreaterThan(note.modifiedLocalAt) { let prepareDate = modificationDate > contentChangeDate ? modificationDate : contentChangeDate if prepareDate > note.modifiedLocalAt { note.modifiedLocalAt = prepareDate } // Trying load content from encrypted note with current password if url.pathExtension == "etp", let password = note.password { _ = note.unLock(password: password) } note.forceLoad() delegate.refreshTextStorage(note: note) } // print("File changed: \(url)") // Not updates in FS attributes, must be loaded from Cloud Drive Meta if note.isTextBundle() { note.loadCreationDate() } else { note.creationDate = creationDate } notesModificationQueue.append(note) //resolveConflict(url: url) continue } // Note previously exist on different path if let note = getNoteFromCloudDriveResults(item: item) { // moved to unavailable dir (i.e. trash) is equal removed guard storage.getProjectByNote(url: url) != nil else { storage.removeNotes(notes: [note], fsRemove: false) {_ in self.notesDeletionQueue.append(note) } print("File moved outside: \(url)") continue } // moved to available dir print("File moved to new url: \(url)") notesDeletionQueue.append(note) let srcUrl = note.url note.url = url note.parseURL() note.moveHistory(src: srcUrl, dst: url) resultsDict[index] = url notesInsertionQueue.append(note) continue } // Non exist yet, will add if let note = storage.importNote(url: url) { notesInsertionQueue.append(note) } } return completed } private func getNoteFromCloudDriveResults(item: NSMetadataItem) -> Note? { let itemUrl = item.value(forAttribute: NSMetadataItemURLKey) as? URL guard let url = itemUrl?.standardized else { return nil } let index = self.metadataQuery.index(ofResult: item) guard let prev = resultsDict[index] as? URL else { return nil } if prev != url { if let note = storage.getBy(url: prev) { return note } } return nil } private func getProjectFromCloudDriveResults(item: NSMetadataItem) -> Project? { let itemUrl = item.value(forAttribute: NSMetadataItemURLKey) as? URL guard let url = itemUrl?.standardized else { return nil } let index = self.metadataQuery.index(ofResult: item) guard let prev = resultsDict[index] as? URL else { return nil } if prev != url { if let project = storage.getProjectBy(url: prev) { return project } } return nil } private func added(notification: NSNotification) -> Int { guard let addedMetadataItems = notification.userInfo?[NSMetadataQueryUpdateAddedItemsKey] as? [NSMetadataItem] else { return 0 } for item in addedMetadataItems { guard let url = (item.value(forAttribute: NSMetadataItemURLKey) as? URL)?.standardized else { continue } print("Added: \(url)") let status = item.value(forAttribute: NSMetadataUbiquitousItemDownloadingStatusKey) as? String if status != NSMetadataUbiquitousItemDownloadingStatusCurrent && FileManager.default.isUbiquitousItem(at: url) { do { try FileManager.default.startDownloadingUbiquitousItem(at: url) } catch { print("Download error: \(error)") } continue } if isProject(item: item) { if let projects = storage.insert(url: url) { projectsInsertionQueue.append(contentsOf: projects) } continue } // when file moved from outspace to FSNotes space // i.e. revert from macOS trash to iCloud Drive if storage.isValidNote(url: url) { if let note = storage.importNote(url: url) { notesInsertionQueue.append(note) } } } return addedMetadataItems.count } private func remove(notification: NSNotification) -> Int { guard let removedMetadataItems = notification.userInfo?[NSMetadataQueryUpdateRemovedItemsKey] as? [NSMetadataItem] else { return 0 } for item in removedMetadataItems { guard let url = (item.value(forAttribute: NSMetadataItemURLKey) as? URL)?.standardized else { continue } if isProject(item: item) { if let project = storage.getProjectBy(url: url) { projectsDeletionQueue.append(contentsOf: [project]) } continue } if url.lastPathComponent == ".encrypt" { self.loadEncryptionStatus(url: url) continue } if FileManager.default.fileExists(atPath: url.path) { continue } if let note = storage.getBy(url: url) { storage.removeNotes(notes: [note], fsRemove: false) {_ in self.notesDeletionQueue.append(note) } } if let project = storage.getProjectBy(url: url) { storage.remove(project: project) self.projectsDeletionQueue.append(project) } } return removedMetadataItems.count } private func loadEncryptionStatus(url: URL) { if let project = self.storage.getProjectBy(url: url.deletingLastPathComponent()) { let state = project.isEncrypted project.isEncrypted = FileManager.default.fileExists(atPath: url.path) if state && !project.isEncrypted { project.password = nil } DispatchQueue.main.async { if let indexPath = self.delegate.sidebarTableView.getIndexPathBy(project: project) { if let sidebarItem = self.delegate.sidebarTableView.getSidebarItem(project: project) { var type: SidebarItemType = .Project if project.isEncrypted { if project.isLocked() { type = .ProjectEncryptedLocked } else { type = .ProjectEncryptedUnlocked } } sidebarItem.setType(type: type) let cell = self.delegate.sidebarTableView.cellForRow(at: indexPath) as? SidebarTableCellView cell?.configure(sidebarItem: sidebarItem) } self.delegate.sidebarTableView.reload(indexPath: indexPath) // Selected at this moment if indexPath == self.delegate.sidebarTableView.indexPathForSelectedRow { if project.isEncrypted && project.isLocked() { self.delegate.enableLockedProject() } else { self.delegate.disableLockedProject() } self.delegate.reloadNotesTable() // Reconfigure new state in menu if let sidebarItem = self.delegate.sidebarTableView.getSidebarItem(project: project) { self.delegate.configureNavMenu(for: sidebarItem) } } } } } } public func resolveConflict(url: URL) { if let conflicts = NSFileVersion.unresolvedConflictVersionsOfItem(at: url as URL) { for conflict in conflicts { guard let modificationDate = conflict.modificationDate else { continue } guard let localizedName = conflict.localizedName else { continue } let localizedUrl = URL(fileURLWithPath: localizedName) let ext = url.pathExtension let name = localizedUrl.deletingPathExtension().lastPathComponent let dateFormatter = ISO8601DateFormatter() dateFormatter.formatOptions = [ .withYear, .withMonth, .withDay, .withTime ] let dateString: String = dateFormatter.string(from: modificationDate) let conflictName = "\(name) (CONFLICT \(dateString)).\(ext)" let to = url.deletingLastPathComponent().appendingPathComponent(conflictName) if FileManager.default.fileExists(atPath: to.path) { conflict.isResolved = true continue } // Reload current encrypted note if let currentNote = delegate.editorViewController?.editArea.note, currentNote.url == url { if let password = currentNote.password, ext == "etp" { _ = currentNote.unLock(password: password) } currentNote.forceLoad() delegate.refreshTextStorage(note: currentNote) } do { try FileManager.default.copyItem(at: conflict.url, to: to) var attributes = [FileAttributeKey : Any]() attributes[.posixPermissions] = 0o777 try FileManager.default.setAttributes(attributes, ofItemAtPath: to.path) } catch let error { print("Conflict resolving error: ", error) } conflict.isResolved = true } } } private func doVisualChanges() { let insert = notesInsertionQueue let delete = notesDeletionQueue let change = notesModificationQueue notesInsertionQueue.removeAll() notesDeletionQueue.removeAll() notesModificationQueue.removeAll() let projectsDeletion = projectsDeletionQueue let projectsInsertion = projectsInsertionQueue projectsDeletionQueue.removeAll() projectsInsertionQueue.removeAll() for note in insert { note.forceLoad(skipCreateDate: false, loadTags: false) } for note in change { note.forceLoad(skipCreateDate: true, loadTags: false) } OperationQueue.main.addOperation { self.delegate.notesTable.removeRows(notes: delete) self.delegate.notesTable.insertRows(notes: insert) self.delegate.notesTable.reloadRows(notes: change) self.delegate.sidebarTableView.removeRows(projects: projectsDeletion) self.delegate.sidebarTableView.insertRows(projects: projectsInsertion) self.delegate.updateNotesCounter() } } } ================================================ FILE: FSNotes iOS/Helpers/FolderPopoverActions.swift ================================================ // // FolderPopoverActions.swift // FSNotes iOS // // Created by Александр on 27.01.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // import Foundation enum FolderPopoverActions: Int { case importNote case multipleSelection case settingsFolder case settingsRepository case createFolder case removeFolder case renameFolder case removeTag case renameTag case openInFiles case emptyBin case encryptFolder case decryptFolder case lockFolder case unLockFolder static let description = [ NSLocalizedString("Import Notes", comment: "Main view popover table"), NSLocalizedString("Select", comment: "Main view popover table"), NSLocalizedString("View Settings", comment: "Main view popover table"), NSLocalizedString("Git Settings", comment: "Main view popover table"), NSLocalizedString("Create Folder", comment: "Main view popover table"), NSLocalizedString("Remove Folder", comment: "Main view popover table"), NSLocalizedString("Rename Folder", comment: "Main view popover table"), NSLocalizedString("Remove Tag", comment: "Main view popover table"), NSLocalizedString("Rename Tag", comment: "Main view popover table"), NSLocalizedString("Open in Files.app", comment: "Main view popover table"), NSLocalizedString("Empty Bin", comment: "Main view popover table"), NSLocalizedString("Encrypt", comment: "Main view popover table"), NSLocalizedString("Decrypt", comment: "Main view popover table"), NSLocalizedString("Lock", comment: "Main view popover table"), NSLocalizedString("Unlock", comment: "Main view popover table"), ] public func getDescription() -> String { return FolderPopoverActions.description[self.rawValue] } } ================================================ FILE: FSNotes iOS/Helpers/SandboxBookmark.swift ================================================ // // SandboxBookmarks.swift // FSNotes // // Created by Олександр Глущенко on 7/28/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Foundation class SandboxBookmark { static var instance: SandboxBookmark? = nil private let bookmarksKey = "SecurityBookmarksKey" private var defaults = UserDefaults.init(suiteName: "group.es.fsnot.user.defaults") private var bookmarks = [URL: Data]() public static func sharedInstance() -> SandboxBookmark { guard let sandbox = self.instance else { self.instance = SandboxBookmark() return self.instance! } return sandbox } public func load() { if let bookmarks = defaults?.object(forKey: bookmarksKey) as? [Data] { for bookmarkData in bookmarks { do { var isStale = false let url = try URL(resolvingBookmarkData: bookmarkData, bookmarkDataIsStale: &isStale) if !isStale { if url.startAccessingSecurityScopedResource() { self.bookmarks[url.standardized] = bookmarkData print("URL loaded from security scope: \(url)") } } else { remove(url: url) } } catch let error { print(error) } } } } public func save(data: Data) { var bookmarks = [Data]() if let bookmarksData = defaults?.object(forKey: bookmarksKey) as? [Data] { bookmarks = bookmarksData } bookmarks.append(data) save(data: bookmarks) } public func save(data: [Data]) { defaults?.set(data, forKey: bookmarksKey) defaults?.synchronize() } public func remove(url: URL) { self.bookmarks.removeValue(forKey: url) // old style bookmarks let oldStylePath = "/private" + url.path if let index = bookmarks.firstIndex(where: { $0.key.path == oldStylePath }) { bookmarks.remove(at: index) } let values = bookmarks.map({ $0.value }) save(data: values) } public func getRestoredUrls() -> [URL] { return bookmarks.map({ $0.key }) } } ================================================ FILE: FSNotes iOS/Helpers/ShortcutIdentifier.swift ================================================ // // ShortcutIdentifier.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 9/15/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation enum ShortcutIdentifier: String { case makeNew case search case clipboard // MARK: - Initializers init?(fullType: String) { guard let last = fullType.components(separatedBy: ".").last else { return nil } self.init(rawValue: last) } // MARK: - Properties var type: String { return Bundle.main.bundleIdentifier! + ".\(self.rawValue)" } } ================================================ FILE: FSNotes iOS/Helpers/Sidebar.swift ================================================ // // Sideabr.swift // FSNotes iOS // // Created by Олександр Глущенко on 02.11.2019. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import UIKit typealias Image = UIImage enum SidebarSection: Int { case System = 0x00 case Projects = 0x01 case Tags = 0x02 case Settings = 0x03 } class Sidebar { let storage = Storage.shared() public var items = [[SidebarItem]]() public var allItems = [[SidebarItem]]() init() { guard let defaultURL = Storage.shared().getDefault()?.url else { return } var system = [SidebarItem]() // Notes let notesUrl = defaultURL.appendingPathComponent("Fake Virtual Notes Dir") let notesLabel = NSLocalizedString("Notes", comment: "Sidebar items") if UserDefaultsManagement.sidebarVisibilityNotes { let fakeNotesProject = Project( storage: Storage.shared(), url: notesUrl, label: notesLabel, isVirtual: true ) Storage.shared().allNotesProject = fakeNotesProject system.append( SidebarItem( name: NSLocalizedString("Notes", comment: ""), project: fakeNotesProject, type: .All ) ) } // Inbox if UserDefaultsManagement.sidebarVisibilityInbox, let project = Storage.shared().getDefault() { system.append( SidebarItem( name: NSLocalizedString("Inbox", comment: ""), project: project, type: .Inbox ) ) } // Todo if UserDefaultsManagement.sidebarVisibilityTodo { let todoUrl = defaultURL.appendingPathComponent("Fake Virtual Todo Dir") let todoLabel = NSLocalizedString("Todo", comment: "Sidebar items") let fakeTodoProject = Project( storage: Storage.shared(), url: todoUrl, label: todoLabel, isVirtual: true ) system.append( SidebarItem( name: NSLocalizedString("Todo", comment: ""), project: fakeTodoProject, type: .Todo ) ) Storage.shared().todoProject = fakeTodoProject } // Untagged if UserDefaultsManagement.sidebarVisibilityUntagged { let todoUrl = defaultURL.appendingPathComponent("Fake Virtual Utagged Dir") let untaggedLabel = NSLocalizedString("Untagged", comment: "") let fakeUntaggedProject = Project( storage: Storage.shared(), url: todoUrl, label: untaggedLabel, isVirtual: true ) let untagged = SidebarItem( name: untaggedLabel, project: fakeUntaggedProject, type: .Untagged ) system.append(untagged) } // Trash if UserDefaultsManagement.sidebarVisibilityTrash { system.append( SidebarItem( name: NSLocalizedString("Trash", comment: ""), project: Storage.shared().getDefaultTrash(), type: .Trash ) ) } // System - section 0 items.append(system) // Projects - section 1 let projects = storage .getAvailableProjects() .sorted(by: { $0.label < $1.label }) .map({ SidebarItem( name: $0.label, project: $0, type: .Project ) }) items.append(projects) // Tags - section 2 items.append([]) } } ================================================ FILE: FSNotes iOS/Helpers/SingleImageTouchDownGestureRecognizer.swift ================================================ // // SingleImageTouchDownGestureRecognizer.swift // FSNotes iOS // // Created by Олександр Глущенко on 8/17/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import UIKit.UIGestureRecognizerSubclass class SingleImageTouchDownGestureRecognizer: UIGestureRecognizer { public var isRightBorderTap = false override func touchesBegan(_ touches: Set, with event: UIEvent) { if touches.count > 1 { self.state = .failed } if self.state == .possible { for touch in touches { guard let view = self.view as? EditTextView else { continue } let point = touch.location(in: view) let glyphIndex = view.layoutManager.glyphIndex(for: point, in: view.textContainer) let location = touch.location(in: view) let maxX = Int(view.frame.width - 50) let minX = Int(50) let isImage = view.isImage(at: glyphIndex) let glyphRect = view.layoutManager.boundingRect(forGlyphRange: NSRange(location: glyphIndex, length: 1), in: view.textContainer) if Int(location.x) < minX || Int(location.x) > maxX, isImage, glyphIndex < view.textStorage.length, glyphRect.contains(point) { self.state = .possible isRightBorderTap = Int(location.x) > maxX } else { self.state = .failed } } } } override func touchesEnded(_ touches: Set, with event: UIEvent) { if self.state == .possible { for touch in touches { guard let view = self.view as? EditTextView else { continue } let point = touch.location(in: view) let glyphIndex = view.layoutManager.glyphIndex(for: point, in: view.textContainer) let location = touch.location(in: view) let maxX = Int(view.frame.width - 25) let minX = Int(25) let isImage = view.isImage(at: glyphIndex) let glyphRect = view.layoutManager.boundingRect(forGlyphRange: NSRange(location: glyphIndex, length: 1), in: view.textContainer) if Int(location.x) < minX || Int(location.x) > maxX, isImage, glyphIndex < view.textStorage.length, glyphRect.contains(point) { self.state = .recognized } else { self.state = .failed } } } } } ================================================ FILE: FSNotes iOS/Helpers/SingleTouchDownGestureRecognizer.swift ================================================ // // SingleTouchDownGestureRecognizer.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 8/30/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit.UIGestureRecognizerSubclass class SingleTouchDownGestureRecognizer: UIGestureRecognizer { private var beginTimer: Timer? private var beginTime: Date? public var isLongPress: Bool = false public var touchCharIndex: Int? public var selectedRange: UITextRange? override func touchesBegan(_ touches: Set, with event: UIEvent) { guard let view = self.view as? EditTextView else { return } self.selectedRange = view.selectedTextRange view.isAllowedScrollRect = false if touches.count > 1 { self.state = .failed view.isAllowedScrollRect = true return } if self.state == .possible { for touch in touches { let point = touch.location(in: view) let characterIndex = view.layoutManager.characterIndex(for: point, in: view.textContainer, fractionOfDistanceBetweenInsertionPoints: nil) self.touchCharIndex = characterIndex if UIMenuController.shared.isMenuVisible { UIMenuController.shared.hideMenu(from: view) } let glyphIndex = view.layoutManager.glyphIndex(for: point, in: view.textContainer) if isTodoInTouchArea(point: point, characterIndex: characterIndex, view: view) && (self.selectedRange?.isEmpty == true || !view.isFirstResponder) { self.state = .recognized return } let maxX = Int(view.frame.width - 50) let minX = Int(50) let isImage = view.isImage(at: characterIndex) let glyphRect = view.layoutManager.boundingRect(forGlyphRange: NSRange(location: glyphIndex, length: 1), in: view.textContainer) if isImage, characterIndex < view.textStorage.length, glyphRect.contains(point) { if Int(point.x) > minX && Int(point.x) < maxX { view.lasTouchPoint = touch.location(in: view.superview) self.state = .possible view.isAllowedScrollRect = true return } else { self.state = .failed view.isAllowedScrollRect = true return } } if !isImage && glyphRect.contains(point) && view.isLink(at: characterIndex) { self.state = .possible view.isAllowedScrollRect = true return } } self.state = .failed view.isAllowedScrollRect = true } } private func isTodoInTouchArea(point: CGPoint, characterIndex: Int, view: EditTextView) -> Bool { if view.isTodo(at: characterIndex) { return true } func checkTodoInLine(lineRange: NSRange) -> Bool { for i in lineRange.location.. 0 { let previousLineRange = (view.text as NSString).lineRange(for: NSRange(location: currentLineRange.location - 1, length: 0)) if checkTodoInLine(lineRange: previousLineRange) { return true } } return false } override func touchesEnded(_ touches: Set, with event: UIEvent) { guard let view = self.view as? EditTextView else { return } if self.state == .possible { for touch in touches { let point = touch.location(in: view) let characterIndex = view.layoutManager.characterIndex(for: point, in: view.textContainer, fractionOfDistanceBetweenInsertionPoints: nil) self.touchCharIndex = characterIndex if view.isImage(at: characterIndex) { self.state = .recognized view.isAllowedScrollRect = true return } if isTodoInTouchArea(point: point, characterIndex: characterIndex, view: view) { self.state = .recognized return } let glyphRect = view.layoutManager.boundingRect(forGlyphRange: NSRange(location: characterIndex, length: 1), in: view.textContainer) if glyphRect.contains(point) && view.isLink(at: characterIndex) { self.state = .recognized view.isAllowedScrollRect = true return } } self.state = .failed view.isAllowedScrollRect = true } } } ================================================ FILE: FSNotes iOS/Icons/classic-2025.icon/icon.json ================================================ { "fill" : { "automatic-gradient" : "extended-gray:1.00000,1.00000" }, "groups" : [ { "layers" : [ { "fill" : { "automatic-gradient" : "srgb:0.00000,0.74277,1.00000,1.00000" }, "image-name" : "Untitled-15.svg", "name" : "row1", "position" : { "scale" : 1, "translation-in-points" : [ -153.79487500000016, 157.33406250000007 ] } }, { "fill" : { "automatic-gradient" : "srgb:0.00000,0.27616,1.00000,1.00000" }, "image-name" : "Untitled-14.svg", "name" : "row2", "position" : { "scale" : 1, "translation-in-points" : [ -248.06381250000013, 32.89393749999999 ] } }, { "fill" : { "automatic-gradient" : "srgb:0.00000,0.27616,1.00000,1.00000" }, "image-name" : "Untitled-13.svg", "name" : "row3", "position" : { "scale" : 1, "translation-in-points" : [ -196.13787500000012, -69.6507187499999 ] } }, { "fill" : { "automatic-gradient" : "srgb:0.00000,0.27616,1.00000,1.00000" }, "image-name" : "Untitled-12.svg", "name" : "row4", "position" : { "scale" : 1, "translation-in-points" : [ -149.16081250000013, -181.203125 ] } }, { "fill" : { "automatic-gradient" : "srgb:0.00000,0.74277,1.00000,1.00000" }, "image-name" : "Untitled-8.svg", "name" : "row5", "position" : { "scale" : 1, "translation-in-points" : [ -7.970875000000149, 54.2109375 ] } }, { "fill" : { "automatic-gradient" : "srgb:0.00000,0.27451,1.00000,1.00000" }, "image-name" : "Untitled-11.svg", "name" : "lightning", "position" : { "scale" : 2, "translation-in-points" : [ 172.49609375, -35.471000000000004 ] } } ], "shadow" : { "kind" : "neutral", "opacity" : 0.5 }, "translucency" : { "enabled" : true, "value" : 0.5 } } ], "supported-platforms" : { "circles" : [ "watchOS" ], "squares" : "shared" } } ================================================ FILE: FSNotes iOS/Icons/modern.icon/icon.json ================================================ { "fill" : "automatic", "groups" : [ { "layers" : [ { "fill-specializations" : [ { "value" : { "automatic-gradient" : "srgb:0.11674,0.11137,0.12082,1.00000" } }, { "appearance" : "dark", "value" : { "automatic-gradient" : "srgb:0.85953,0.87116,0.86212,1.00000" } } ], "image-name" : "Untitled-5.svg", "name" : "row1", "position" : { "scale" : 1, "translation-in-points" : [ -76.92312500000003, -199.8671875 ] } }, { "fill-specializations" : [ { "value" : { "automatic-gradient" : "srgb:0.11674,0.11137,0.12082,1.00000" } }, { "appearance" : "dark", "value" : { "automatic-gradient" : "srgb:0.85953,0.87116,0.86212,1.00000" } } ], "image-name" : "Untitled-2 3.svg", "name" : "row2", "position" : { "scale" : 1, "translation-in-points" : [ -136.953125, -51.15625 ] } }, { "fill-specializations" : [ { "value" : { "automatic-gradient" : "srgb:0.11674,0.11137,0.12082,1.00000" } }, { "appearance" : "dark", "value" : { "automatic-gradient" : "srgb:0.85953,0.87116,0.86212,1.00000" } } ], "image-name" : "Untitled-3 2.svg", "name" : "row3", "position" : { "scale" : 1, "translation-in-points" : [ -199.593125, 98.390625 ] } }, { "fill" : { "solid" : "display-p3:0.02353,0.56863,1.00000,1.00000" }, "image-name" : "Untitled-3 2.svg", "name" : "row4", "position" : { "scale" : 1, "translation-in-points" : [ 200.44268750000003, -51.15625 ] } }, { "fill" : { "solid" : "display-p3:0.02353,0.56863,1.00000,1.00000" }, "image-name" : "Untitled-2 3.svg", "name" : "row5", "position" : { "scale" : 1, "translation-in-points" : [ 137.8046875, 98.390625 ] } }, { "fill" : { "solid" : "display-p3:0.02353,0.56863,1.00000,1.00000" }, "glass" : true, "image-name" : "Untitled-3 2.svg", "name" : "row6", "position" : { "scale" : 1, "translation-in-points" : [ 75.16468750000001, 247.140625 ] } } ], "shadow" : { "kind" : "neutral", "opacity" : 0.5 }, "translucency" : { "enabled" : true, "value" : 0.5 } } ], "supported-platforms" : { "circles" : [ "watchOS" ], "squares" : "shared" } } ================================================ FILE: FSNotes iOS/Icons/ny-2026.icon/icon.json ================================================ { "fill" : { "linear-gradient" : [ "extended-srgb:0.68627,0.32157,0.87059,1.00000", "display-p3:0.00000,0.66275,0.87843,1.00000" ], "orientation" : { "start" : { "x" : 0.5, "y" : 0 }, "stop" : { "x" : 0.5, "y" : 0.7 } } }, "groups" : [ { "layers" : [ { "fill" : { "automatic-gradient" : "extended-gray:1.00000,1.00000" }, "image-name" : "document-lightning-24-regular.svg", "name" : "document-lightning-24-regular", "opacity" : 0.9, "position-specializations" : [ { "idiom" : "square", "value" : { "scale" : 1.4, "translation-in-points" : [ 23.2528076171875, -6.20391845703125 ] } } ] } ], "shadow" : { "kind" : "neutral", "opacity" : 0.5 }, "translucency" : { "enabled" : true, "value" : 0.5 } } ], "supported-platforms" : { "squares" : "shared" } } ================================================ FILE: FSNotes iOS/ImagePreviewViewController.swift ================================================ // // ViewController.swift // ImageScrollViewDemo // // Created by Nguyen Cong Huy on 3/5/16. // Copyright © 2016 Nguyen Cong Huy. All rights reserved. // import UIKit import AudioToolbox import CropViewController class ImagePreviewViewController: UIViewController, CropViewControllerDelegate { @IBOutlet weak var imageScrollView: ImageScrollView! public var image: UIImage? public var url: URL? public var note: Note? public var imageUrls = [URL]() private var currentIndex = 0 override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: UIDevice.orientationDidChangeNotification, object: nil) let shareButton = Buttons.getShare(target: self, selector: #selector(share)) let cropButton = Buttons.getCrop(target: self, selector: #selector(crop)) let dropButton = Buttons.getTrash(target: self, selector: #selector(trashBin)) let space = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) space.width = 30 let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) toolbarItems = [flexibleSpace, shareButton, space, cropButton, space, dropButton] navigationController?.setToolbarHidden(false, animated: true) DispatchQueue.main.async { self.rotated() } let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapGestureRecognizer(_:))) tapGesture.numberOfTapsRequired = 1 imageScrollView.addGestureRecognizer(tapGesture) var urls = [URL]() if let result = note?.content.getImagesAndFiles() { for item in result { urls.append(item.url) } } imageUrls = urls if let url = url, let index = imageUrls.firstIndex(of: url) { currentIndex = index } } @objc public func tapGestureRecognizer(_ gestureRecognizer: UIGestureRecognizer) { let center = gestureRecognizer.location(in: gestureRecognizer.view) var selectedUrl: URL? if let zoomView = imageScrollView.zoomView, zoomView.frame.width > view.frame.width { return } if center.x < 40 { selectedUrl = prevImage() } if center.x > imageScrollView.frame.width - 40 { selectedUrl = nextImage() } if let url = selectedUrl, let data = try? Data(contentsOf: url), let image = UIImage(data: data) { imageScrollView.display(image: image) self.image = image self.url = selectedUrl } } private func nextImage() -> URL { currentIndex += 1 if currentIndex >= imageUrls.count { currentIndex = 0 } return imageUrls[currentIndex] } private func prevImage() -> URL { currentIndex -= 1 if currentIndex < 0 { currentIndex = imageUrls.count - 1 } return imageUrls[currentIndex] } @IBAction func share() { guard let url = self.url else { return } AudioServicesPlaySystemSound(1519) let objectsToShare = [url] as [Any] let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil) activityVC.excludedActivityTypes = [UIActivity.ActivityType.addToReadingList] if UIDevice.current.userInterfaceIdiom == .pad { guard let popOver = activityVC.popoverPresentationController else { return } popOver.permittedArrowDirections = .down popOver.sourceView = view popOver.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0) } present(activityVC, animated: true, completion: nil) } @IBAction func done() { dismiss(animated: true, completion: nil) } @IBAction func crop() { guard let image = image else { return } let cropViewController = CropViewController(image: image) cropViewController.delegate = self cropViewController.hidesNavigationBar = false cropViewController.modalPresentationStyle = .fullScreen cropViewController.modalTransitionStyle = .crossDissolve cropViewController.transitioningDelegate = nil present(cropViewController, animated: true, completion: nil) } @IBAction func trashBin() { let messageText = NSLocalizedString("Are you sure you want to delete this image?", comment: "") let alertController = UIAlertController(title: NSLocalizedString("Picture removing", comment: ""), message: messageText, preferredStyle: .alert) let confirmAction = UIAlertAction(title: "OK", style: .default) { (_) in if let url = self.url, let _ = self.note, let textView = UIApplication.getEVC().editArea { if let imageRange = textView.textStorage.getImageRange(url: url) { textView.selectedRange = imageRange if let should = textView.delegate?.textView?(textView, shouldChangeTextIn: imageRange, replacementText: "") { guard should else { return } } textView.insertAttributedText(NSAttributedString(string: "")) } } self.navigationController?.popViewController(animated: true) } let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) { (_) in } alertController.addAction(confirmAction) alertController.addAction(cancelAction) self.present(alertController, animated: true, completion: nil) } @objc func rotated() { imageScrollView.setup() imageScrollView.imageContentMode = .aspectFit imageScrollView.initialOffset = .center imageScrollView.display(image: image!) } func cropViewController(_ cropViewController: CropViewController, didCropToImage image: UIImage, withRect cropRect: CGRect, angle: Int) { if let url = url { try? image.jpegData(compressionQuality: 0.6)?.write(to: url) let cacheImage = NoteAttachment.getCacheUrl(from: url, prefix: "ThumbnailsBigInline") if let path = cacheImage?.path, FileManager.default.fileExists(atPath: path) { try? FileManager.default.removeItem(at: cacheImage!) } UIApplication.getEVC().refill() } self.imageScrollView.display(image: image) if let windowScene = UIApplication.shared.connectedScenes .filter({ $0.activationState == .foregroundActive }) .compactMap({ $0 as? UIWindowScene }) .first, let rootViewController = windowScene.windows.first(where: { $0.isKeyWindow })?.rootViewController { navigationController?.navigationBar.isTranslucent = true rootViewController.dismiss(animated: true, completion: nil) } } func cropViewController(_ cropViewController: CropViewController, didFinishCancelled cancelled: Bool) { navigationController?.navigationBar.isTranslucent = true dismiss(animated: true, completion: nil) } } extension UIBarButtonItem { static func menuButton(_ target: Any?, action: Selector, imageName: String, size:CGSize = CGSize(width: 32, height: 32), tintColor:UIColor?) -> UIBarButtonItem { let button = UIButton(type: .system) button.tintColor = tintColor button.setImage(UIImage(named: imageName), for: .normal) button.addTarget(target, action: action, for: .touchUpInside) let menuBarItem = UIBarButtonItem(customView: button) menuBarItem.customView?.translatesAutoresizingMaskIntoConstraints = false menuBarItem.customView?.heightAnchor.constraint(equalToConstant: size.height).isActive = true menuBarItem.customView?.widthAnchor.constraint(equalToConstant: size.width).isActive = true return menuBarItem } } ================================================ FILE: FSNotes iOS/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName FSNotes CFBundleDocumentTypes CFBundleTypeExtensions textbundle CFBundleTypeIconFiles TextBundle.icns CFBundleTypeMIMETypes text/x-textbundle CFBundleTypeName TextBundle CFBundleTypeRole Editor LSHandlerRank Alternate LSItemContentTypes org.textbundle.package CFBundleTypeExtensions md markdown mmd multimarkdown mdown mkdn mkd mdwn mdtxt mdtext mdml CFBundleTypeMIMETypes text/x-markdown CFBundleTypeName Markdown document CFBundleTypeRole Editor LSHandlerRank Alternate LSItemContentTypes net.daringfireball.markdown CFBundleExecutable $(EXECUTABLE_NAME) CFBundleGetInfoString CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName FSNotes CFBundlePackageType APPL CFBundleShortVersionString $(MARKETING_VERSION) CFBundleURLTypes CFBundleTypeRole Viewer CFBundleURLIconFile makeNote CFBundleURLName co.fluder.mobile.FSNotes-iOS CFBundleURLSchemes fsnotes CFBundleTypeRole Viewer CFBundleURLIconFile makeNote CFBundleURLName co.fluder.mobile.FSNotes-iOS CFBundleURLSchemes nv CFBundleTypeRole Viewer CFBundleURLIconFile makeNote CFBundleURLName co.fluder.mobile.FSNotes-iOS CFBundleURLSchemes nvalt CFBundleVersion $(CURRENT_PROJECT_VERSION) LSApplicationCategoryType public.app-category.productivity LSRequiresIPhoneOS LSSupportsOpeningDocumentsInPlace NSAppTransportSecurity NSAllowsArbitraryLoads NSCameraUsageDescription Needs for attach photo in notes NSFaceIDUsageDescription Note encryption and decryption with FaceID NSHumanReadableCopyright NSLocationWhenInUseUsageDescription Requested when photo attached NSPhotoLibraryAddUsageDescription Needs permission to write images in photos app NSPhotoLibraryUsageDescription Needs for attach images in notes NSPrivacyAccessedAPITypes NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategoryActiveKeyboards NSPrivacyAccessedAPITypeReasons 54BD.1 NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategoryFileTimestamp NSPrivacyAccessedAPITypeReasons DDA9.1 NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategorySystemBootTime NSPrivacyAccessedAPITypeReasons 35F9.1 NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategoryUserDefaults NSPrivacyAccessedAPITypeReasons CA92.1 NSUbiquitousContainers iCloud.co.fluder.fsnotes NSUbiquitousContainerIsDocumentScopePublic NSUbiquitousContainerSupportedFolderLevels One NSUserActivityTypes es.fsnot.handoff-open-note UIAppFonts SourceCodePro-Bold.ttf SourceCodePro-Black.ttf SourceCodePro-Regular.ttf SourceCodePro-It.ttf SourceCodePro-BoldIt.ttf UIApplicationSceneManifest UIApplicationSupportsMultipleScenes UISceneConfigurations UIWindowSceneSessionRoleApplication UISceneConfigurationName Default Configuration UISceneDelegateClassName $(PRODUCT_MODULE_NAME).SceneDelegate UISceneStoryboardFile Main UIBackgroundModes fetch remote-notification UIFileSharingEnabled UILaunchStoryboardName Launch Screen UIMainStoryboardFile Main UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportsDocumentBrowser UIViewControllerBasedStatusBarAppearance UTExportedTypeDeclarations UTTypeConformsTo com.apple.package UTTypeDescription TextBundle UTTypeIconFiles TextBundle.icns UTTypeIdentifier org.textbundle.package UTTypeTagSpecification public.filename-extension textbundle UTTypeConformsTo public.data UTTypeDescription Markdown UTTypeIconFiles Markdown.icns UTTypeIdentifier net.daringfireball.markdown UTTypeTagSpecification public.filename-extension md markdown ================================================ FILE: FSNotes iOS/InfoPlist.xcstrings ================================================ { "sourceLanguage" : "en", "strings" : { "CFBundleDisplayName" : { "comment" : "Bundle display name", "extractionState" : "extracted_with_value", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "FSNotes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } } } }, "CFBundleGetInfoString" : { "comment" : "Get Info string", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "" } } } }, "CFBundleName" : { "comment" : "Bundle name", "extractionState" : "extracted_with_value", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "FSNotes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } } } }, "Markdown" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Markdown" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Markdown" } } } }, "NSCameraUsageDescription" : { "comment" : "Privacy - Camera Usage Description", "extractionState" : "extracted_with_value", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Potřeba pro přidávání fotek do poznámek" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sie müssen ein Foto zu Notizen hinzufügen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Needs for attach photo in notes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Needs for attach photo in notes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट्स में फोटो संलग्न करने की आवश्यकता" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "E' necessario per poter aggiungere foto nelle note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "撮影した写真をノートに追加できるようにするため" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Behoeften voor het bijvoegen van foto in notities" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Necessário para anexar fotos às notas" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Потреби вкладати фото в нотатки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "需要在笔记中附加照片" } } } }, "NSFaceIDUsageDescription" : { "comment" : "Privacy - Face ID Usage Description", "extractionState" : "extracted_with_value", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Šifrování a dešifrování poznámek pomocí Face ID" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ver- und Entschlüsselung mit FaceID" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Note encryption and decryption with FaceID" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Note encryption and decryption with FaceID" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FaceID के साथ नोट्स एन्क्रिप्शन और डिक्रिप्शन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Crittografia e decrittografia delle note con FaceID" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FaceIDによるノートの暗号化・復号化のため" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Let op codering en decodering met FaceID" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Criptografar e descriptografar com FaceID" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Шифрування та дешифрування за допомогою FaceID" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "注意使用 Face 进行加密和解密" } } } }, "NSHumanReadableCopyright" : { "comment" : "Copyright (human-readable)", "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "" } } } }, "NSLocationWhenInUseUsageDescription" : { "comment" : "Privacy - Location When In Use Usage Description", "extractionState" : "extracted_with_value", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vyžádáno při připojení fotky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Wird beim Anhängen eines Fotos angefordert" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Requested when photo attached" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Requested when photo attached" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फोटो संलग्न होने पर अनुरोध" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "E' richiesto quando viene allegata una foto" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "画像を挿入した際にリクエストされます" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Gevraagd wanneer foto bijgevoegd" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Necessário quando existem fotos anexadas" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Потрібен дозвіл коли додається фотографія у нотатку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "附上照片时请求" } } } }, "NSPhotoLibraryAddUsageDescription" : { "comment" : "Privacy - Photo Library Additions Usage Description", "extractionState" : "extracted_with_value", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Potřeba pro ukládání fotek do knihovny" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erfordert eine Berechtigung zum Aufzeichnen von Bildern in der Foto-App" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Needs permission to write images in photos app" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Needs permission to write images in photos app" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ोटो ऐप में चित्र लिखने के लिए अनुमति की आवश्यकता" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "E' necessario il permesso in scrittura per salvare immagini nell'app Foto" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "挿入した画像をライブラリに保存する際に必要です" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Heeft toestemming nodig om afbeeldingen in de foto-app te schrijven" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Precisa de permissão para escrever imagens no aplicativo de fotos" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Потрібен дозвіл на запис зображень у додаток для фотографій" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "需要权限才能在照片应用中写入图像" } } } }, "NSPhotoLibraryUsageDescription" : { "comment" : "Privacy - Photo Library Usage Description", "extractionState" : "extracted_with_value", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Potřeba pro připojení obrázků do poznámek" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erfordert das Anhängen von Bildern an Notizen" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "Needs for attach images in notes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Needs for attach images in notes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट्स में चित्र संलग्न करने की आवश्यकता" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "E' necessario per allegare immagini nelle note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "画像をノートに挿入する際に必要です" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Behoeften voor het toevoegen van afbeeldingen in notities" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Necessário para anexar imagens às notas" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Потрібен дозвіл коли додається зображення у нотатку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "需要在笔记中附加图像" } } } }, "TextBundle" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle" } } } } }, "version" : "1.0" } ================================================ FILE: FSNotes iOS/Launch Screen.storyboard ================================================ ================================================ FILE: FSNotes iOS/LaunchImage.launchimage/Contents.json ================================================ { "images": [ { "extent": "full-screen", "idiom": "iphone", "subtype": "2436h", "filename": "Default1125x2436.png", "minimum-system-version": "11.0", "orientation": "portrait", "scale": "3x" }, { "extent": "full-screen", "idiom": "iphone", "subtype": "2436h", "filename": "Default2436x1125.png", "minimum-system-version": "11.0", "orientation": "landscape", "scale": "3x" }, { "orientation": "landscape", "idiom": "tv", "filename": "Default3840x2160.png", "extent": "full-screen", "minimum-system-version": "11.0", "scale": "2x" }, { "orientation": "landscape", "idiom": "tv", "filename": "Default1920x1080.png", "extent": "full-screen", "minimum-system-version": "9.0", "scale": "1x" }, { "extent": "full-screen", "idiom": "iphone", "subtype": "736h", "filename": "Default1242x2208.png", "minimum-system-version": "8.0", "orientation": "portrait", "scale": "3x" }, { "extent": "full-screen", "idiom": "iphone", "subtype": "736h", "filename": "Default2208x1242.png", "minimum-system-version": "8.0", "orientation": "landscape", "scale": "3x" }, { "extent": "full-screen", "idiom": "iphone", "subtype": "667h", "filename": "Default750x1334.png", "minimum-system-version": "8.0", "orientation": "portrait", "scale": "2x" }, { "orientation": "portrait", "idiom": "iphone", "filename": "Default640x960.png", "extent": "full-screen", "minimum-system-version": "7.0", "scale": "2x" }, { "extent": "full-screen", "idiom": "iphone", "subtype": "retina4", "filename": "Default640x1136.png", "minimum-system-version": "7.0", "orientation": "portrait", "scale": "2x" }, { "orientation": "portrait", "idiom": "ipad", "filename": "Default768x1024.png", "extent": "full-screen", "minimum-system-version": "7.0", "scale": "1x" }, { "orientation": "landscape", "idiom": "ipad", "filename": "Default1024x768.png", "extent": "full-screen", "minimum-system-version": "7.0", "scale": "1x" }, { "orientation": "portrait", "idiom": "ipad", "filename": "Default1536x2048.png", "extent": "full-screen", "minimum-system-version": "7.0", "scale": "2x" }, { "orientation": "landscape", "idiom": "ipad", "filename": "Default2048x1536.png", "extent": "full-screen", "minimum-system-version": "7.0", "scale": "2x" }, { "orientation": "portrait", "idiom": "iphone", "filename": "Default320x480.png", "extent": "full-screen", "scale": "1x" }, { "orientation": "portrait", "idiom": "iphone", "filename": "Default640x960.png", "extent": "full-screen", "scale": "2x" }, { "orientation": "portrait", "idiom": "iphone", "filename": "Default640x1136.png", "extent": "full-screen", "subtype": "retina4", "scale": "2x" }, { "orientation": "portrait", "idiom": "ipad", "filename": "Default768x1024.png", "extent": "full-screen", "scale": "1x" }, { "orientation": "landscape", "idiom": "ipad", "filename": "Default1024x768.png", "extent": "full-screen", "scale": "1x" }, { "orientation": "portrait", "idiom": "ipad", "filename": "Default1536x2048.png", "extent": "full-screen", "scale": "2x" }, { "orientation": "landscape", "idiom": "ipad", "filename": "Default2048x1536.png", "extent": "full-screen", "scale": "2x" } ], "info": { "version": 1, "author": "fanstudio" } } ================================================ FILE: FSNotes iOS/Localizable.xcstrings ================================================ { "sourceLanguage" : "en", "strings" : { "..." : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "…" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "..." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "..." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "..." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "..." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "..." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "..." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "..." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "..." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "…" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "..." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "..." } } } }, "+" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "+" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "+" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "+" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "+" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "+" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "+" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "+" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "+" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "+" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "+" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "+" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "+" } } } }, "✅ - " : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "✅ -" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "✅ - " } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "✅ - " } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "✅ - " } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "✅ - " } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "✅ - " } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "✅ - " } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "✅ - " } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "✅ - " } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "✅ -" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "✅ - " } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "✅ - " } } } }, "Add External Folder" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přidat externí složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Externer Ordner" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dossier externe" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बाह्य फ़ोल्डर जोड़ें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cartella esterna" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "外部フォルダを追加" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Externe map" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pasta Externa" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Добавить внешнюю папку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Harici Klasör Ekle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зовнішня папка" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "添加外部文件夹" } } } }, "Advanced" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Pokročilé" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Profi" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Avancé" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "उन्नत" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Esteso" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "プロ" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verlengd" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Estendido" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Расширенные" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Gelişmiş" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розширені" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "高级" } } } }, "Are you sure you want to delete all versions of this note?" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Určitě chcete smazat všechny verze této poznámky?" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Möchten Sie wirklich alle Versionen dieser Notiz löschen?" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Voulez-vous vraiment supprimer toutes les versions de cette note ?" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्या आप वाकई इस नोट के सभी संस्करण हटाना चाहते हैं?" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sei sicuro di voler eliminare tutte le versioni di questa nota?" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "このノートの全バージョンを削除しますか?" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Weet u zeker dat u alle versies van deze notitie wilt verwijderen?" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tem certeza de que deseja excluir todas as versões desta nota?" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вы уверены, что хотите удалить все версии этой заметки?" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bu notun tüm sürümlerini silmek istediğinizden emin misiniz?" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ви впевнені, що хочете видалити всі версії цієї нотатки?" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "是否确定要删除此笔记的所有历史版本?" } } } }, "Are you sure you want to delete the history of all notes?" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Určitě chcete smazat historii všech poznámek?" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Möchten Sie den Verlauf aller Notizen wirklich löschen?" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Voulez-vous vraiment supprimer l'historique de toutes les notes ?" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्या आप वाकई सभी नोट्स का इतिहास हटाना चाहते हैं?" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sei sicuro di voler eliminare la cronologia di tutte le note?" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "すべてのノートの履歴を削除しますか?" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Weet u zeker dat u de geschiedenis van alle notities wilt verwijderen?" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tem certeza de que deseja excluir o histórico de todas as notas?" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вы уверены, что хотите удалить историю всех заметок?" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tüm notların geçmişini silmek istediğinizden emin misiniz?" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ви впевнені, що хочете видалити історію всіх нотаток?" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "是否确定要删除所有笔记的历史版本?" } } } }, "Are you sure you want to delete this image?" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Určitě chcete smazat tento obrázek?" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Möchten Sie dieses Bild wirklich löschen?" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Êtes-vous sûr de vouloir supprimer cette image ?" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्या आप वाकई में इस छवि को हटाना चाहते हैं?" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sei sicuro di voler eliminare questa immagine?" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "この画像を削除しますか?" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Weet je zeker dat je deze afbeelding wilt verwijderen?" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tem certeza de que deseja apagar esta imagem?" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вы уверены, что хотите удалить это изображение?" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bu resmi silmek istediğinizden emin misiniz?" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ви впевнені, що хочете видалити це зображення?" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "是否确定要删除此图像?" } } } }, "Ascending" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vzestupně" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Aufsteigend" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ascendant" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "आरोही" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Crescente" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "昇順" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Oplopend" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ascendente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Восходящее" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Artan" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Висхідний" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "升序" } } } }, "Auto Rename By Title" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Podle nadpisu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Nach Titel umbenennen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Renommer automatiquement par titre" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक द्वारा स्वतः नाम बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina per titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タイトルをファイル名に自動反映" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Naam wijzigen op titel" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomeação automática por título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Авто переименование " } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlığa Göre Otomatik Yeniden Adlandır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Автоматичне перейменування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "按内容第一行自动重命名" } } } }, "Autocorrection" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Automatická oprava" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Autokorrektur" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Autocorrection" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्वतः सुधार" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Autocorrezione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "自動訂正" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Autocorrectie" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Auto correção" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Автокоррекция" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Otomatik Düzeltme" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Автокорекція" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "自动校正" } } } }, "Autoname By Title" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Automatické pojmenování podle názvu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Automatische Benennung nach Titel" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dénomination automatique par titre" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक के अनुसार स्वचालित नामकरण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Denominazione automatica per titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タイトルによる自動命名" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Automatische naamgeving op titel" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nomeação automática por título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Автоименование по заголовку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlığa Göre Otomatik Adlandırma" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Автоіменування за заголовком" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "按标题自动命名" } } } }, "build" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "build" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "version" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "build" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "निर्माण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "build" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ビルド" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "build" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "versão" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "сборка" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "білд" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "构建" } } } }, "Cancel" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zrušit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Abbrechen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Annuler" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "रद्द करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Annulla" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "キャンセル" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Annuleer" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Cancelar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Отменить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İptal" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скасувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "取消" } } } }, "Change Creation Date" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Změnit datum vytvoření" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Datum erstellt" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Modifier la Date de création" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "निर्माण तिथि बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Data di creazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "作成日" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Datum gecreeërd" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Data Criada" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Изменить дату создания" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Oluşturma Tarihini Değiştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Дата створення" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更改创建日期" } } } }, "Check Spelling" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kontrolovat pravopis" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Rechtschreibung prüfen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Vérifier l’orthographique" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्पेलिंग जांचे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Controllo ortografico" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "スペルチェック" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Spellingcheck" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Verificação Ortográfica" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Проверять орфографию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yazımı Denetle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перевірка правопису" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "检查拼写" } } } }, "Code" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kód" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Code" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Code" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोड" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Codice" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "コード" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Code" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Código" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Код" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kod" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Код" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "代码字体" } } } }, "Code Block Live Highlighting" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Živé zvýraznění kódu " } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Code hervorheben" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mise en surbrillance automatique des blocs de code" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोड ब्लॉक लाइव हाइलाइटिंग" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Evidenzia il blocco di codice attivo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "コードブロックの色付け" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Codeblok live markering" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Realçar bloco de código" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Живая подсветка кода" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kod Bloğu Canlı Vurgulama" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Живе підсвічування блоку коду" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "代码块实时高亮" } } } }, "Code Theme" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Motiv kódu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Code-Design" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Thème de codes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोड थीम" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Tema del codice" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "コードテーマ" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Code thema" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tema do código" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Код темы" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kod Teması" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Тема коду" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "代码主题" } } } }, "Compatible with Bear and Ulysses (textbundle), markdown, txt." : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kompatibilní s Bear a Ulysses (textbundle), markdown, txt." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Kompatibel mit Bear und Ulysses (Textbundle), markdown, txt." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Compatible avec Bear et Ulysses (textbundle), markdown, txt." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Bear और Ulysses (textbundle), markdown, txt के साथ संगत।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Compatibile con Bear and Ulysses (textbundle), markdown, txt." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Bear and Ulysses (textbundle), markdown, txtと互換性があります" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Compatibel met Bear en Ulysses (textbundle), markdown, txt." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Compatível com Bear e Ulysses (textbundle), markdown, txt." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Совместим с Bear и Ulysses (textbundle), markdown, txt." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bear ve Ulysses (textbundle), markdown, txt ile uyumludur." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сумісний з Bear and Ulysses (textbundle), markdown, txt." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "兼容 Bear(熊掌记) and Ulysses (textbundle),markdown,txt." } } } }, "Container" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kontejner" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Container" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Container" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कंटेनर" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Container" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "コンテナ" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Container" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Container" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Контейнер" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kapsayıcı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Контейнер" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "容器" } } } }, "Copy Plain Text" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Kopírovat prostý text" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Text kopieren" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copier le texte brut" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सादा पाठ कॉपी करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copia testo semplice" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "プレインテキストとしてコピー" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Kopieer platte tekst" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Copiar texto simples" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Скопировать обычный текст" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Düz Metni Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скопіювати звичайний текст" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "拷贝纯文本" } } } }, "Create Folder" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vytvořit složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner erstellen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Créer le dossier" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फोल्डर बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Crea cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新規フォルダ" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Map aanmaken" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Criar pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать папку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasör Oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Створити папку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "创建文件夹" } } } }, "Create folder:" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vytvořit složku:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner erstellen:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Créer le dossier :" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फोल्डर बनाएं:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Crea cartella:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新規フォルダ:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Map aanmaken:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Criar pasta:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать папку:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasör oluştur:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Створити папку:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "创建文件夹:" } } } }, "Create Web Page" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vytvořit webovou stránku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Web-Seite erstellen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Créer une page Web" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब पेज बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Crea pagina web" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ウェブページの作成" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Create Web Page" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Criar página da Web" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать веб-страницу" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web Sayfası Oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Створити веб-сторінку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "创建网页" } } } }, "Creation Date" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Datum vytvoření" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erstelldatum" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Date de création" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "निर्माण तिथि" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Data di creazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "作成日" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Aanmaakdatum" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Data de criação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Дата создания" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Oluşturma Tarihi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Датою створення" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "创建日期" } } } }, "Decrypt" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Dešifrovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Entschlüsseln" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déchiffrer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डिक्रिप्ट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Decriptare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Decrypt" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Decrypt" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Desencriptar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Расшифровать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Şifresini Çöz" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розшифрувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "解密" } } } }, "Default Keyboard" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Výchozí klávesnice" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Standard-Tastatur" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Clavier par défaut" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डिफ़ॉल्ट कीबोर्ड" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Tastiera predefinita" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "エディタで使用するデフォルトキーボード" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Standaard toetsenbord" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Teclado padrão" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Клавиатура по умолчанию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Varsayılan Klavye" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Клавіатура за замовчуванням" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "默认键盘" } } } }, "Delete" : { "comment" : "Table row action", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Smazat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Löschen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Elimina" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "削除" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verwijderen" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Eliminar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sil" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除" } } } }, "Delete Web Page" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Smazat webovou stránku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Web-Seite löschen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer la page Web" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब पेज हटाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Elimina pagina web" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ウェブページを削除する" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Webpagina verwijderen" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Excluir página da Web" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить веб-страницу" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web Sayfasını Sil" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити веб-сторінку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除网页" } } } }, "Descending" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Sestupně" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Absteigend" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Descendant" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अवरोही" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Decrescente" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "降順" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Aflopend" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Descendente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Нисходящее" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Azalan" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Низхідний" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "降序" } } } }, "Documents" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Dokumenty" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Dokumente" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Documents" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "दस्तावेज़" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Documenti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ドキュメント" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Documenten" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Documentos" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Документы" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Belgeler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Документи" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "文件" } } } }, "Done" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hotovo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erledigt" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Fait" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हो गया" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Fatto" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Done" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Gedaan" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Feito" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Готово" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tamamlandı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Готово" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "完成" } } } }, "Duplicate" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Duplikovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Duplikat" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dupliquer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डुप्लिकेट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Duplicare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "複製" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Duplicaat" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Duplicada" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Дублировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Çoğalt" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Створити копію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "生成副本" } } } }, "Dynamic Type" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Dynamické písmo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Dynamische Schriftart" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Type dynamique" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गतिशील प्रकार" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Tipizzazione dinamica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ダイナミックタイプ" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Dynamisch Type" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tipo dinâmico" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Динамический шрифт" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dinamik Tür" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Динамічний шрифт" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "动态类型" } } } }, "Editor" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Editor" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Editorin" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Éditeur" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संपादक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Editrice" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "エディタ" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Editor" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Editor" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Редактор" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Düzenleyici" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Редактор" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "编辑" } } } }, "Empty Bin" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vyprázdnit koš" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Leerer Behälter" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Vider la Corbeille" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कूडा खाली करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cestino vuoto" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ゴミ箱を空にする" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Lege bak" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Caixa vazia" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Очистить корзину" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Çöp Kutusunu Boşalt" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Очистити кошик" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "清空回收站" } } } }, "Encrypt" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zašifrovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Verschlüsseln Sie" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Chiffrer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "एन्क्रिप्ट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Crittografare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "エンクリプト" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Versleutel" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Encriptar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Зашифровать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Şifrele" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зашифрувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "加密" } } } }, "Enter folder name" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zadejte název složky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordnername eingeben" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Entrez le nom du dossier" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ोल्डर का नाम दर्ज करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Inserisci il nome della cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォルダ名を入力" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Ga naar onze mape" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Digite o nome da pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Введите имя папки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasör adını girin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Введіть назву папки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "输入文件夹名称" } } } }, "Enter Master Password" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zadejte hlavní heslo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passwort eingeben" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Entrez le mot de passe principal" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मास्टर पासवर्ड दर्ज करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Inserisci la Master Password" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "マスターパスワードを入力" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voer het hoofdwachtwoord in" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Introduzir Palavra-passe Mestra" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Введите мастер-пароль" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ana Parolayı Girin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Введіть пароль" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "输入主密码" } } } }, "Enter new tag name" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zadejte název nové značky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Tag eingeben" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Entrez un nouveau nom d’étiquette" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नया टैग नाम दर्ज करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Interisci il nuovo nome del tag" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新規タグ名を入力" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voer een nieuwe tagnaam in" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Insira o novo nome da tag" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Введите новое имя тега" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni etiket adını girin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Введіть назву тега" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "输入新标签名称" } } } }, "Enter note name" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zadejte název poznámky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notizname eingeben" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Entrez le nom de la note" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट का नाम दर्ज करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Inserisci il nome della nota" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ノート名を入力" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voer notitie naam in" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Introduzir nome da nota" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Введите название заметки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Not adını girin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Введіть назву нотатки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "输入备注名称" } } } }, "Extension" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přípona" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Dateiformat" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Extension" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "एक्सटेंशन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Estensione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "拡張子" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Uitbreiding" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Extensão" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Расширение" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Uzantı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розширення" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "文件后缀格式" } } } }, "Family" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Rodina" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Schriftfamilie" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Famille" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "परिवार" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Famiglia di font" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ファミリー" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Lettertype familie" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Família de fontes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Семья" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Aile" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сімейство шрифтів" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "字体集" } } } }, "File with this name already exist" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Soubor s tímto názvem už existuje" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Eine Datei mit diesem Namen existiert bereits" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Un fichier avec ce nom existe déjà" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इस नाम वाली फ़ाइल पहले से मौजूद है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Il file con questo nome già esiste" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "同名のファイルがすでに存在しています" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Bestand met deze naam bestaat al" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Já existe um ficheiro com este nome" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Файл с таким именем уже существует" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bu isimde dosya zaten mevcut" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Файл з цим іменем вже існує" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "具有此名称的文件已存在" } } } }, "Files" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Soubory" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Datei Format" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Fichiers" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ाइल" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Formato del file" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ファイル形式" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Bestandsformaat" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Formato de arquivo" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Файлы" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dosyalar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Файли" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "文件" } } } }, "Files Naming" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Názvy souborů" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Dateinamen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nommage des fichiers" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ाइलों का नामकरण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Denominazione dei file" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ファイル命名" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Naamgeving van bestanden" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nomenclatura de arquivos" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Именование файлов" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Dosya Adlandırma" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Іменування файлів" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "文件命名" } } } }, "Folder name:" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Název složky:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordnername:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nom de dossier :" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ोल्डर का नाम:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nome cartella:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォルダ名:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Naam van de map:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nome da pasta:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Название папки:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasör adı:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Назва папки:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "文件夹名称:" } } } }, "Folder with this name already exist" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Složka s tímto názvem už existuje" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ein Ordner mit diesem Namen existiert bereits" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Un dossier avec ce nom existe déjà" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इस नाम वाला फ़ोल्डर पहले से मौजूद है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "La cartella con questo nome già esiste" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "同名のフォルダがすでに存在しています" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Map met deze naam bestaat al" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Já existe uma pasta com este nome" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Папка с таким именем уже существует" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bu isimde klasör zaten mevcut" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Папка з цим іменем вже існує" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "具有此名称的文件夹已存在" } } } }, "Folders" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Složky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dossiers" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ोल्डर" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cartelle" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "プロジェクト" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Mappen" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pastas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Папки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasörler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Папки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "文件夹" } } } }, "Font" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Písmo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Schrift" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Police" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ॉन्ट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Carattere" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォント" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Lettertype" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Letra" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Шрифт" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yazı Tipi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Шрифт" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "字体" } } } }, "Font Family" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Rodina písma" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Schriftart" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Famille de police" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ॉन्ट परिवार" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Tipo carattere" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォントファミリー" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Lettertypefamilie" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Família do tipo de letra" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Семейство шрифтов" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yazı Tipi Ailesi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сімейство шрифтів" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "字体集" } } } }, "Font Size" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Velikost písma" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Schriftgröße" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Taille de police" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ॉन्ट आकार" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Dimensione carattere" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォントサイズ" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Lettertypegrootte" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tamanho da letra" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Размер шрифта" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yazı Tipi Boyutu" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розмір шрифту" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "字体大小" } } } }, "Format: Untitled Note" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Formát: Poznámka bez názvu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Format: Untitled Note" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Format : Note sans titre" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रारूप: शीर्षक रहित नोट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Formato: Untitled Note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォーマット: 無題のノート" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Formaat: Untitled Note" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Formato: Untitled Note" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Формат: Без названия" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Biçim: Başlıksız Not" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Формат: Untitled Note" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "格式:无标题笔记" } } } }, "Format: yyyy-MM-dd hh.mm.ss a" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Formát: yyyy-MM-dd hh.mm.ss a" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Format: yyyy-MM-dd hh.mm.ss a" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Format : yyyy-MM-dd hh.mm.ss a" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रारूप: yyyy-MM-dd hh.mm.ss a" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Formato: yyyy-MM-dd hh.mm.ss a" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォーマット: yyyy-MM-dd hh.mm.ss a" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Formaat: yyyy-MM-dd hh.mm.ss a" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Formato: yyyy-MM-dd hh.mm.ss a" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Формат: yyyy-MM-dd hh.mm.ss a" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Biçim: yyyy-AA-gg hh.dd.ss a" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Формат: yyyy-MM-dd hh.mm.ss a" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "格式: yyyy-MM-dd hh.mm.ss a" } } } }, "Format: yyyyMMddHHmmss" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Formát: yyyyMMddHHmmss" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Format: yyyyMMddHHmmss" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Format : yyyyMMddHHmmss" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रारूप: yyyyMMddHHmmss" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Formato: yyyyMMddHHmmss" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォーマット: yyyyMMddHHmmss" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Formaat: yyyyMMddHHmmss" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Formato: yyyyMMddHHmmss" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Формат: yyyyMMddHHmmss" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Biçim: yyyyAAggSSddss" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Формат: yyyyMMddHHmmss" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "格式: yyyyMMddHHmmss" } } } }, "FSNotes" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "FSNotları" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "FSNotes" } } } }, "General" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Obecné" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Allgemein" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Général" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सामान्य" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Generali" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "一般" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Algemeen" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Geral" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Главные" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Genel" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Загальні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "常规" } } } }, "Git" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गिट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Git" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Git" } } } }, "Git Add/commit/push" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Git přidat/commit/push" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Git Add/commit/push" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Git Add/commit/push" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गिट जोड़ें/कमिट/पुश" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Git Add/commit/push" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Git Add/commit/push" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Git Add/commit/push" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Git Add/commit/push" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Git Add/commit/push" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Git Ekle/Gönder/Gönder" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Git Add/commit/push" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Git 添加/提交/推送" } } } }, "Git Settings" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nastavení Gitu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Git settings" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Paramètres git" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गिट प्राथमिकताएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Git settings" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Git settings" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Git settings" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Git settings" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Настройки Git" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Git Ayarları" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Налаштування Git" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Git 设置" } } } }, "History" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Historie" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Geschichte" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Histoire" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इतिहास" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Storia" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "履歴" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Geschiedenis" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "História" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "История" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Geçmiş" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Історія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "笔记历史版本" } } } }, "iCloud Drive" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "iCloud ड्राइव" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Sürücüsü" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "iCloud Drive" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "iCloud 云同步" } } } }, "Icon" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Ikona" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Icon" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Icône" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "आइकन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Icon" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アイコン" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Icoon" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ícone" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Иконка" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Simge" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Іконка програми" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "图标" } } } }, "Images source:" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zdroj obrázků:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bildquelle:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Source des images :" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "चित्र स्रोत:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Fonti delle immagini:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "画像ソース:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Afbeeldingen bron:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Fonte das imagens:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Источник изображений:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Görüntü kaynağı:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Джерело зображень:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "图片来源:" } } } }, "Import Notes" : { "comment" : "Main view popover table\nSettings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Importovat poznámky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Importieren" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Importer des notes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट्स आयात करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Importa note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ノートのインポート" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Importeren notities" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Importar notas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Импортировать заметки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notları İçe Aktar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Імпортувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "导入笔记" } } } }, "Inbox" : { "comment" : "Inbox in sidebar", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Příchozí" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Eingang" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Boîte de réception" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इनबॉक्स" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "In Entrata" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "未整理" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Postvak In" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Caixa de entrada" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Входящие" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Gelen Kutusu" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вхідні" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "收集箱" } } } }, "Insert" : { }, "Invalid Password" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Neplatné heslo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ungültiges Passwort" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mot de passe incorrect" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अवैध पासवर्ड" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Password non valida" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "無効なパスワードです" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Ongeldig wachtwoord" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Palavra-passe inválida" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Неверный пароль" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Geçersiz Parola" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Недійсний пароль" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "密码无效" } } } }, "Library" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Knihovna" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Seitenleiste" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Barre latérale" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लाइब्रेरी" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Barra laterale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サイドバー" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zijbalk" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Barra lateral" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Библиотека" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kütüphane" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Бічна панель" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "文库" } } } }, "Line Spacing" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Řádkování" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Zeilenabstand" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Interligne" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पंक्ति रिक्ति" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Interlinea" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "行間隔" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Regelafstand" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Espaçamento entre linhas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Расстояние между строками" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Satır Aralığı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Міжрядковий інтервал" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "行距" } } } }, "Live Images Preview" : { "comment" : "Settings", "extractionState" : "stale", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Živý náhled obrázků" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bilder аnzeigen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aperçu des images" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लाइव छवियाँ पूर्वावलोकन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Antemprima delle immagini" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ライブ画像プレビュー" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon afbeeldingen in de editor" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pré-visualização de imagens" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Предпросмотр изображений" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Canlı Görüntüler Önizlemesi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Живий перегляд зображень" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "实况图像预览" } } } }, "Loading..." : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Načítání..." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Laden..." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Chargement..." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लोड हो रहा है..." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Caricamento..." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "読み込み中..." } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Aan het laden..." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Carregando..." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Загрузка..." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yükleniyor..." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Завантаження..." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "正在加载..." } } } }, "Lock" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zamknout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sperren" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Verrouiller" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लॉक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Blocca" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ロック" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Vergrendel" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Trancar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заблокировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kilit" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заблокувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "上锁" } } } }, "Master" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hlavní" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Master" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Maître" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मास्टर" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Master" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "マスター" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Master" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Master" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Мастер" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ana" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Головний" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "管理员" } } } }, "Master password:" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hlavní heslo:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Master Passwort:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mot de passe maître :" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "मास्टर पासवर्ड:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Password principale:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "マスターパスワード:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Master wachtwoord:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Senha mestra:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Мастер-пароль:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ana parola:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Головний пароль:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "主密码:" } } } }, "MathJax" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "MathJax" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "MathJax" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "MathJax" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "MathJax" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "MathJax" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "MathJax" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "MathJax" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "MathJax" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "MathJax" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "MathJax" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "MathJax" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "MathJax 数学公式" } } } }, "Modification Date" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Datum změny" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Änderungsdatum" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Date de modification" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सुधार की तारीख" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Data di modifica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "変更日" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wijzigingsdatum" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Data de modificação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Дата модификации" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Değişiklik Tarihi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Датою модифікації" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "修改日期" } } } }, "More" : { "comment" : "Table row action", "extractionState" : "stale", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Více" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Mehr" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Plus" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अधिक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Altro" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "他" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Nog steeds" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Todavía" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ещё" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Daha Fazla" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ще" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更多" } } } }, "Move" : { "comment" : "Move view", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přesunout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bewegen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déplacer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्थानांतरण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sposta" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "移動" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verplaats" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mover" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переместить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Taşı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перемістити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "移动" } } } }, "New Note" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nová poznámka" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neues Dokument" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nouvelle Note" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नया नोट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuovo documento" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新規ドキュメント" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Nieuw document" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Novo Documento" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Новая заметка" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni Not" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нова нотатка" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "新建笔记" } } } }, "None" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Žádné" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Standard" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aucun" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोई भी नहीं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Predefinita" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "なし" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Standaard" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Por padrão" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Нет" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Hiçbiri" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "За замовчуванням" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无" } } } }, "Notes" : { "comment" : "Notes in sidebar\nSidebar items\nSidebar label", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Poznámky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notizen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Notes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "すべて" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Notities" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Notas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заметки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notlar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нотатки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "所有笔记" } } } }, "Notes List" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Seznam poznámek" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notizenliste" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Liste de notes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट्स सूची" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Lista note" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ノートリスト" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Notitielijst" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Lista de notas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Список заметок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notlar Listesi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Лист нотаток" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "笔记列表" } } } }, "Open in Files.app" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Otevřit v aplikaci Soubory" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In Files.app öffnen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir dans Files.app" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Files.app में खोलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apri in Files.app" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ファイルで開く" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Openen in Files.app" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Abra em Files.app" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открыть в Files.app" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Files.app'te aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відкрийте в Files.app" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在文件应用打开所在位置" } } } }, "Open Note" : { "comment" : "Document opened", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Otevřít poznámku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Geöffnete Notiz" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir la note" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट खोलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apri nota" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ノートを開く" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Open notitie" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Abrir nota" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открыть заметку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notu aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відкрита нотатка" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "打卡笔记" } } } }, "Passphrase" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přístupová fráze" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासफ्रेज" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Passphrase" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пассфраза" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Parola" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Парольна фраза" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "密码锁" } } } }, "Password" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Heslo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passwort" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mot de passe" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासवर्ड" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Password" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスワード" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wachtwoord" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Palavra-passe" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пароль" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Parola" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Пароль" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "密码" } } } }, "Password has been successfully changed" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Heslo úspěšně změněno" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Das Passwort wurde erfolgreich geändert" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Le mot de passe a été modifié avec succès" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासवर्ड सफलतापूर्वक बदल दिया गया है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "La password è stata modificata con successo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスワードの変更に成功しました" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wachtwoord is succesvol gewijzigd" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "A palavra-passe foi alterada com êxito" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пароль был успешно изменен" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Parola başarıyla değiştirildi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Пароль успішно змінено" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "密码已被成功修改" } } } }, "Password:" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Heslo:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passwort:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mot de passe :" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासवर्ड:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Password:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスワード" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wachtwoord:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Palavra-passe:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пароль:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Parola:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Пароль:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "密码:" } } } }, "Photos" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Fotky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Fotos" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Photos" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Photos" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Foto" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "画像" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Foto's" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Fotos" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Фото" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Fotoğraflar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Фотографії" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "照片" } } } }, "Picture removing" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odebírání fotek" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bild entfernen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Suppression de l'image" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "चित्र हटाया जा रहा है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rimozione dell'immagine" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "削除する画像" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Foto verwijderen" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Remoção de imagem" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удаление изображения" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Resim kaldırılıyor" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалення зображення" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "图片删除" } } } }, "Pin" : { "comment" : "Table row action", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Připnout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Fixieren" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Épingler" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पिन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Fissa" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "メモをピンで固定" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Pin" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Fixar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Закрепить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Pin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Закріпити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "笔记置顶" } } } }, "Please enter valid password" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Prosím zadejte platné heslo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bitte geben Sie ein gültiges Passwort ein" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Merci de saisir un mot de passe correct" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कृपया वैध पासवर्ड दर्ज करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Per favore inserisci una password valida" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "有効なパスワードを入力してください" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Voer a.u.b. geldig wachtwoord in" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Por favor insira uma palavra-passe válida" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пожалуйста, введите правильный пароль" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Lütfen geçerli parolayı girin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Введіть дійсний пароль" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "请输入有效密码" } } } }, "Please try again" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zkuste to znovu" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bitte versuchen Sie es erneut" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Veuillez réessayer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कृपया पुन: प्रयास करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Si prega di riprovare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "再度お試しください" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Probeer het opnieuw" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tente novamente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пожалуйста, попробуйте еще раз" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Lütfen tekrar deneyin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Будь ласка, спробуйте ще разn" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "请再试一试" } } } }, "Private Key" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Soukromý klíč" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Clé privée" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "निजी चाबी" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Private key" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Приватный ключ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Özel Anahtar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Приватний ключ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "私密密钥" } } } }, "Project removing ❌" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odebírání projektu ❌" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Projekt entfernen ❌" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Suppression de projet ❌" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रोजेक्ट हटाया जा रहा है ❌" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rimozione progetto ❌" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "削除するプロジェクト ❌" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Project verwijderen ❌" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Remover projeto❌" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Устранение проекта ❌" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Proje kaldırılıyor ❌" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалення проекту ❌" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "项目删除 ❌" } } } }, "Public Key (optional)" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Veřejný klíč (nepovinné)" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Public key (optional)" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Clé public (facultatif)" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सार्वजनिक कुंजी (वैकल्पिक)" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Public key (opzionale)" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Public key (任意)" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Public key (optionele)" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Public key (facultativo)" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открытый ключ (необязательно)" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Genel Anahtar (isteğe bağlı)" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відкритий ключ (необов'язково)" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "公共密钥 (可选)" } } } }, "Pull (every 30 sec)" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Pull (každých 30 sekund)" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Pull (alle 30 Sekunden)" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Pull (toutes les 30 secondes)" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खींचें (प्रत्येक 30 सेकंड)" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Pull (ogni 30 secondi)" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Pull (30秒ごと)" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Pull (elke 30 sec)" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pull (a cada 30 segundos)" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Pull (каждые 30 секунд)" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Çek (her 30 saniyede bir)" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Тягнути (кожні 30 сек)" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Pull (每隔 30 秒)" } } } }, "Remove Encryption" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odebrat šifrování" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Verschlüsselung entfernen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer le chiffrage" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "एन्क्रिप्शन हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rimuovi la crittografia" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ロックを削除" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verwijder versleuteling" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Remover criptografia" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить шифрование" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Şifrelemeyi kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зняти шифрування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "移除加密" } } } }, "Remove Folder" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odebrat složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner enfernen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer le dossier" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ोल्डर हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rimuovi cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォルダの削除" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verwijder map" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Remover pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить папку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasörü kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити папку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "移除文件夹" } } } }, "Remove Tag" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odebrat značku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Tag entfernen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Enlever l’étiquette" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टैग हटाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rimuovi tag" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "削除されたタグ" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Tag verwijderen" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Remover tag" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить тег" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etiketi kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити тег" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "移除标签" } } } }, "Rename" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přejmenovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Umbenennen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Renommer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नाम बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "名称変更" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hernoemen" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomear" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeniden adlandır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейменувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重命名" } } } }, "Rename Folder" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přejmenovat složku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner umbenennen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Renommer le dossier" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ोल्डर का नाम बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina cartella" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォルダの名称変更" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Naam map wijzigen" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomear pasta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать папку" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasörü yeniden adlandır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейменувати папку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重命名文件夹" } } } }, "Rename folder:" : { "comment" : "Popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přejmenovat složku:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ordner löschen:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Renommer le dossier :" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़ोल्डर का नाम बदलें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina cartella:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フォルダの名称変更:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Naam map wijzigen:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomear pasta:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать папку:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasörü yeniden adlandır:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать папку:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重命名文件夹:" } } } }, "Rename note:" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přejmenovat poznámku:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notiz umbenennen:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Renommer la note" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नोट का नाम बदलें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina nota:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ノートの名称変更:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hernoem notitie:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomear nota:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать заметку:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notu yeniden adlandır:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейменувати нотатку:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重命名笔记:" } } } }, "Rename Tag" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přejmenovat značku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Tag umbenennen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Renommer l’étiquette" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टैग का नाम बदलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina tag" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タグの名称変更" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hernoem tag" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomear tag" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать тег" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etiketi yeniden adlandır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перейменувати тег" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重命名标签" } } } }, "Rename tag:" : { "comment" : "Popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Přejmenovat značku:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Tag umbenennen:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Renommer l’étiquette :" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टैग का नाम बदलें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rinomina tag:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タグの名称変更:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Hernoem tag:" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Renomear tag:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать тег:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etiketi yeniden adlandır:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Переименовать тег:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重命名标签:" } } } }, "Save" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Uložit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Speichern Sie" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Enregistrer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सहेजें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Risparmiare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "セーブ" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Save" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Guardar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сохранить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kaydet" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зберегти" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "保存" } } } }, "Save Clipboard" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Uložit schránku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Kopieren" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Enregistrer le presse-papiers" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्लिपबोर्ड सहेजें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Salva gli appunti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "クリップボードの内容を保存" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Bewaar klembord" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Salvar área de transferência" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сохранить буфер обмена" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Panoya kaydet" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зберегти буфер обміну" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "保存剪贴板" } } } }, "Save Revision" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Uložit úpravy" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Revision speichern" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sauvegarder la révision" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संशोधन सहेजें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Salva revisione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "バージョンを保存" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Bewaar revisie" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Guardar revisão" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сохранить версию" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Revizyonu kaydet" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зберегти ревізію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "保存修订版" } } } }, "Saved versions" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Uložené verze" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Gespeicherte Versionen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Versions enregistrées" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सहेजे गए संस्करण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Versioni salvate" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "保存されたバージョン" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Opgeslagen versies" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Versões salvas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сохраненные версии" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kaydedilen sürümler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Збережені версії" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "保存的历史版本" } } } }, "Search or create" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat / vytvořit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Suchen oder erstellen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Rechercher ou créer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खोजें या बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cerca o crea" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "検索または新規作成" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zoek en creëer" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Pesquisar e criar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Найти или создать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ara veya oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Знайти або створити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "搜索或创建" } } } }, "Search or Create" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Hledat / vytvořit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Suchen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Rechercher ou Créer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खोजें या बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cerca" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "検索" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zoeken" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Procurar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Найти или создать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ara veya Oluştur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Знайти або створити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "搜索或创作" } } } }, "Security" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zabezpečení" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sicherheit" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Securité" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सुरक्षा" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sicurezza" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "セキュリティ" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Beveiliging" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Segurança" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Безопасность" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Güvenlik" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Безпека" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "安全性" } } } }, "Select" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Vybrat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Select" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sélectionner" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "चुने" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Selezionare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "選択" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Selecteer" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Selecionar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Выбрать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вибрати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "选择" } } } }, "Settings" : { "comment" : "Sidebar settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nastavení" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einstellungen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Paramètres" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्राथमिकताएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Impostazioni" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "設定" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Instellingen" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Definições" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Настройки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ayarlar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Налаштування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "设置" } } } }, "Share" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Sdílet" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Teilen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Partager" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "शेयर करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Condividi" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "共有" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Delen" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Partilhar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Поделиться" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Paylaş" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Поділитися" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "分享" } } } }, "Show Folder in Library" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit složku v Knihovně" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Projekt in Seitenleiste anzeigen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher le dossier dans la barre latérale" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लाइब्रेरी में फ़ोल्डर दिखाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Visualizza cartella nella sidebar" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サイドバーにフォルダを表示" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon map in zijbalk" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar pasta na barra lateral" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать папку в библиотеке" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Klasörü Kitaplıkta göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати папку на бічній панелі" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在文库中显示文件夹" } } } }, "Show Notes in \"Notes\" and \"Todo\"" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit poznámky v „Poznámkách“ a „Úkolech“" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Notizen in \"Notizen\" und \"Todo\" anzeigen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher les notes dans \"Notes\" et \"Tâches\"" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "\"नोट्स\" और \"टुडू\" में नोट्स दिखाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Visualizza le note negli elenchi \"Note\" e \"Da Fare\"" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "\"すべて\" と \"タスク\" にノートを表示" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Toon notities in \"Notes\" en \"Todo\" lijsten" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar notas nas listas \"Notas\" e \"Tarefas\"" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показывать в \"Заметках\" и \"Тодо\"" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Notları \"Notlar\" ve \"Yapılacaklar\"da göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Включити у \"Нотатки/завдання\"" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "显示笔记在 \"笔记\" 和 \"待办\"" } } } }, "Sort By" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Řadit podle" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sortieren nach" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Trier par" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इसके अनुसार क्रमबद्ध करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ordina per" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "表示順序" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Sorteer op" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Ordenar por" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сортировать по" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sırala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сортувати за" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "排序方式" } } } }, "Sort Direction" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Směr" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Richtung" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Direction de tri" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्रमबद्ध दिशा" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Direzione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "方向" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Richting" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Direcção" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Направление" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sıralama Yönü" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Напрямок" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "方向" } } } }, "Storage" : { "comment" : "Settings", "extractionState" : "manual", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Úložiště" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Speicherort" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Storage" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Stockage" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संग्रहण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Biblioteca" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ストレージ" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Opslag" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Armazenamento" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Хранилище" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Depolama" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сховище" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "存储" } } } }, "Support" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Podpora" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Support" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aide" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सहायता" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Supporto" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サポート" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Ondersteuning" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Suporte" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Поддержка" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Destek" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Підтримка" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "支持" } } } }, "Tags" : { "comment" : "Sidebar label", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Značky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Tags" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Étiquettes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टैग" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Tag" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タグ" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Tags" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Etiquetas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Теги" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etiketler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Мітки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "标签" } } } }, "Thanks" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Díky" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Dank an" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Remerciement " } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "धन्यवाद" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Grazie a" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "次に感謝:" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Dankzij" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Graças a" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Спасибо" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Teşekkürler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Дякую" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "感谢" } } } }, "Theme" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Motiv" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Thema" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Thème" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "थीम" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Tema" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "テーマ" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Thema" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tema" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Тема" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tema" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Тема" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "主题" } } } }, "Tip: To use old notes, you must decrypt them separately with the old key and encrypt them again." : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Tip: pro použití starých poznámek je musíte nejprve zvlášť dešifrovat pomocí starého klíče a pak znovu zašifrovat." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Tipp: Um alte Notizen zu verwenden, müssen Sie sie separat mit dem alten Schlüssel entschlüsseln und erneut verschlüsseln." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Conseil : pour utiliser d'anciennes notes, vous devez les déchiffrer séparément avec l'ancienne clé et les chiffrer à nouveau." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सलाह: पुराने नोट्स का उपयोग करने के लिए, आपको उन्हें पुरानी कुंजी से अलग से डिक्रिप्ट करना होगा और पुनः एन्क्रिप्ट करना होगा।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Suggerimento: per utilizzare le vecchie note, è necessario decifrarle separatamente con la vecchia chiave e crittografarle nuovamente." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "補足:古いノートを使うには、古いキーで別途復号化し、再度暗号化する必要があります。" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Tip: Om oude notities te gebruiken, moet je ze apart ontsleutelen met de oude sleutel en opnieuw versleutelen." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Sugestão: Para utilizar notas antigas, tem de as desencriptar separadamente com a chave antiga e encriptá-las novamente." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Совет: Чтобы использовать старые заметки, необходимо отдельно расшифровать их с помощью старого ключа и снова зашифровать." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İpucu: Eski notları kullanabilmek için, eski anahtarla ayrı ayrı şifresini çözüp tekrar şifrelemeniz gerekmektedir." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Порада: Щоб використовувати старі нотатки, ви повинні розшифрувати їх окремо за допомогою старого ключа і зашифрувати знову." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "小贴士:要使用旧的笔记,您必须用旧的密钥单独解密,并再次加密。" } } } }, "Title" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nadpis" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Titel" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Titre" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タイトル" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Titel" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Заголовок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Başlık" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Заголовком" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "标题" } } } }, "Todo" : { "comment" : "Sidebar items\nTodo in sidebar", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Úkoly" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Todo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Tasks" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "टुडू" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Da Fare" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タスク" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Te Doen" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Tarefa" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Задачи" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yapılacaklar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Завдання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "待办事项" } } } }, "Trash" : { "comment" : "Sidebar label\nTrash in sidebar", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Koš" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Papierkorb" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Corbeille" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कूडा" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cestino" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ゴミ箱" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Prullenmand" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Lixo" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Корзина" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Çöp" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сміття" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "回收站" } } } }, "Unlock" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odemknout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Entsperren" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déverrouiller" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अनलॉक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sblocca" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アンロック" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Ontgrendel" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Destrancar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Разблокировать" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kilidi aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розблокувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "开锁" } } } }, "Unpin" : { "comment" : "Table row action", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Odepnout" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Loslösen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Désépingler" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अनपिन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sblocca" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ピン固定の解除" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "VerwijderPin" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Soltar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открепить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sabitlemeyi kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відкріпити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "取消置顶" } } } }, "Untagged" : { "comment" : "Sidebar settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Neoznačené" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ungetaggt" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Non étiqueté" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बिना टैग" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Senza tag" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "タグなし" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Niet getagd" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Sem etiqueta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Без метки" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etiketsiz" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Без тегів" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "未标记" } } } }, "Update" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Aktualizovat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Update" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mise à jour" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अद्यतन करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Aggiornamento" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アップデート" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Update" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Actualização" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Обновить" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Güncelleme" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Оновити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更新" } } } }, "Update Web Page" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Aktualizovat webovou stránku" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Geteilte aktualisieren" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mettre à jour la page Web" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब पेज अपडेट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Aggiorna la pagina web" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Web ページの更新" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Webpagina bijwerken" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Atualizar página da Web" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Обновить веб-страницу" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web Sayfasını Güncelle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Оновити веб-сторінку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更新网页" } } } }, "Use First Line as Title" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Použít první řádek jako nadpis" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erste Zeile als Titel" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Utiliser la première ligne comme titre" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "शीर्षक के रूप में प्रथम पंक्ति का उपयोग करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Usa la prima riga come titolo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "最初の行をタイトルとして使用する" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Gebruik eerste regel als titel" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Utilizar a primeira linha como título" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Первая строка как заголовок" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İlk Satırı Başlık Olarak Kullan" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перший рядок як заголовок" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "使用第一行作为标题" } } } }, "Use Inline Tags" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Používat značky mezi textem" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Inlinetags verwenden" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Utiliser les étiquettes en ligne" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इनलाइन टैग का उपयोग करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Usa tag in linea" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インラインタグを使用する" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Gebruik inline tags" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Utilizar etiquetas em linha" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Используйте встроенные теги" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Satır İçi Etiketleri Kullan" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Використовувати вбудовані теги" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "使用内联标签" } } } }, "Use TextBundle info.json to store c/mtime" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Použití TextBundle info.json pro uložení c/mtime" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "TextBundle info.json zum Speichern von c/mtime verwenden" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Utilisez TextBundle info.json pour stocker c/mtime" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "c/mtime संग्रहीत करने के लिए TextBundle info.json का उपयोग करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Usare TextBundle info.json per memorizzare c/mtime" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "c/mtimeを格納するためにTextBundle info.jsonを使用する。" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Gebruik TextBundle info.json om c/mtime op te slaan" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Utilizar TextBundle info.json para armazenar c/mtime" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Использовать TextBundle info.json для хранения c/mtime" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "C/mtime'ı depolamak için TextBundle info.json'ı kullanın" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Використовувати TextBundle info.json для зберігання c/mtime" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "使用 TextBundle info.json 存储 c/mtime" } } } }, "Verify Password" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Ověřit heslo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passwort überprüfen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Vérifier le mot de passe" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासवर्ड को सत्यापित करें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Verifica della password" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスワードの確認" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Wachtwoord verifiëren" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Verificar a palavra-passe" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Проверить пароль" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Şifreyi Doğrula" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Підтвердіть пароль" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "验证密码" } } } }, "Version" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Verze" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Version" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Version" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संस्करण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Versione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "バージョン" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Versie" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Versão" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Версия" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Versiyon" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Версія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "版本" } } } }, "View" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Zobrazit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sicht" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Voir" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "देखें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Visualizzazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "表示" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Visie" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Visualizar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вид" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Görünüm" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вид" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "视图" } } } }, "View Settings" : { "comment" : "Main view popover table", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nastavení zobrazení" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einstellungen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Paramètres d'affichage" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्राथमिकताएं देखें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Impostazioni vista" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "設定を開く" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Bekijk instellingen" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Definições de visualização" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Настройки вида" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Görünüm Ayarları" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Налаштування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "查看设置" } } } }, "Visibility" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Viditelnost" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sichtbarkeit" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Visibilité" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "दृश्यता" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Visibilità" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "可視性" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Zichtbaarheid" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Visibilidade" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Видимость" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Görünürlük" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видимість" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "可见性" } } } }, "Web sharing error" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Chyba sdílení na web" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Web sharing error" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Erreur de partage web" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेब साझाकरण त्रुटि" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Web sharing error" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Web sharing error" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Web sharing error" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Web sharing error" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ошибка публикации веб-страниц" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web paylaşım hatası" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Web sharing error" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "网站分享错误" } } } }, "Website" : { "comment" : "Settings", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Webová stránka" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Startseite" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Site web" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "वेबसाइट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Homepage sito web" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ホームページ" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Startpagina" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Página inicial" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сайт" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Web Site" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Домашня сторінка" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "官方网站" } } } }, "Wrong password" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nesprávné heslo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Falsches Passwort" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mot de passe erroné" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गलत पासवर्ड" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Wrong password" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスワードが違う" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verkeerd wachtwoord" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Palavra-passe errada" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Неверный пароль" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yanlış şifre" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Неправильний пароль" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "密码错误" } } } }, "Wrong repeated password" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nesprávné opakované heslo" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Falsches wiederholtes Passwort" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mot de passe répété erroné" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "गलत दोहराया गया पासवर्ड" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Wrong repeated password" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "繰り返しのパスワードが違う" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Verkeerd herhaald wachtwoord" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Palavra-passe repetida errada" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Неправильный пароль" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yanlış tekrarlanan şifre" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Неправильний повторний пароль" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重复密码错误" } } } }, "Сlearing history" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Čištění historie" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Verlauf löschen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Effacement de l'historique" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इतिहास साफ़ किया जा रहा है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sto imparando la storia" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "履歴の削除" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Geschiedenis leren" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Aprendendo a história" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Очистить историю" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Geçmişi temizleme" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Очищення історії" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "清空此笔记的历史版本" } } } } }, "version" : "1.0" } ================================================ FILE: FSNotes iOS/Main.storyboard ================================================ ================================================ FILE: FSNotes iOS/MainNavigationController.swift ================================================ // // MainNavigationController.swift // FSNotes iOS // // Created by Александр on 23.01.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // import UIKit class MainNavigationController: UINavigationController { override func popViewController(animated: Bool) -> UIViewController? { UserDefaultsManagement.currentNote = nil topViewController?.view.endEditing(true) return super.popViewController(animated: animated) } // override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { // super.traitCollectionDidChange(previousTraitCollection) // // guard traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) else { return } // // DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { // let evc = UIApplication.getEVC() // // evc.editArea.textStorage.updateCheckboxList() // // if let previewView = evc.getPreviewView() { // let funcName = self.traitCollection.userInterfaceStyle == .dark ? "switchToDarkMode" : "switchToLightMode" // let switchScript = "if (typeof(\(funcName)) == 'function') { \(funcName)(); }" // // previewView.evaluateJavaScript(switchScript) // } // } // } } ================================================ FILE: FSNotes iOS/MoveViewController.swift ================================================ // // MoveViewController.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 9/8/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit class MoveViewController: UITableViewController { private var projects: [Project]? private var selectedNotes: [Note] private var notesTableView: NotesTableView init(notes: [Note], notesTableView: NotesTableView) { self.selectedNotes = notes self.notesTableView = notesTableView super.init(style: .plain) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { self.navigationItem.rightBarButtonItem = Buttons.getAdd(target: self, selector: #selector(createFolder)) self.projects = Storage.shared().getProjects() self.title = NSLocalizedString("Move", comment: "Move view") super.viewDidLoad() } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let vc = notesTableView.viewDelegate else { return } if let projects = self.projects { let project = projects[indexPath.row] for note in selectedNotes { let dstURL = project.url.appendingPathComponent(note.name) if note.project != project { note.moveImages(to: project) vc.sidebarTableView.removeTags(in: [note]) guard note.move(to: dstURL) else { let alert = UIAlertController(title: "Oops 👮‍♂️", message: NSLocalizedString("File with this name already exist", comment: ""), preferredStyle: UIAlertController.Style.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)) self.present(alert, animated: true, completion: nil) note.moveImages(to: note.project) return } note.moveHistory(src: note.url, dst: dstURL) note.url = dstURL note.parseURL() note.project = project self.notesTableView.removeRows(notes: [note]) vc.notesTable.insertRows(notes: [note]) } } UIApplication.getVC().updateNotesCounter() } UIApplication.getEVC().updateTitle() self.dismiss(animated: true, completion: nil) } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell() if let projects = self.projects { let project = projects[indexPath.row] if project.isTrash { cell.textLabel?.text = NSLocalizedString("Trash", comment: "") } else { cell.textLabel?.text = project.getNestedLabel() } } return cell } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if let projects = self.projects { return projects.count } return 0 } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if selectedNotes.count == 1 { let note = selectedNotes.first! if let projects = self.projects { if projects[indexPath.row] == note.project { cell.accessoryType = .checkmark } } } } @objc func createFolder() { let alertController = UIAlertController(title: NSLocalizedString("Folder name:", comment: ""), message: nil, preferredStyle: .alert) alertController.addTextField { (textField) in textField.placeholder = "" } let confirmAction = UIAlertAction(title: "OK", style: .default) { (_) in guard let name = alertController.textFields?[0].text, name.count > 0 else { return } guard let allProjects = self.projects, allProjects.first(where: { $0.label == name } ) == nil else { let alert = UIAlertController(title: "Oops 👮‍♂️", message: NSLocalizedString("Folder with this name already exist", comment: ""), preferredStyle: UIAlertController.Style.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)) self.present(alert, animated: true, completion: nil) return } guard let newDir = UserDefaultsManagement.storageUrl?.appendingPathComponent(name) else { return } do { try FileManager.default.createDirectory(at: newDir, withIntermediateDirectories: false, attributes: nil) } catch { print(error) return } if let projects = Storage.shared().insert(url: newDir) { OperationQueue.main.addOperation { UIApplication.getVC().sidebarTableView.insertRows(projects: projects) self.projects?.append(contentsOf: projects) self.tableView.reloadData() self.notesTableView.viewDelegate?.sidebarTableView.reloadSidebar() } } } let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) { (_) in } alertController.addAction(confirmAction) alertController.addAction(cancelAction) self.present(alertController, animated: true, completion: nil) } @objc func cancel() { self.dismiss(animated: true, completion: nil) } } ================================================ FILE: FSNotes iOS/Preferences/AppIconViewController.swift ================================================ // // AppIconViewController.swift // FSNotes iOS // // Created by Oleksandr Hlushchenko on 07.04.2023. // Copyright © 2023 Oleksandr Hlushchenko. All rights reserved. // import UIKit class AppIconViewController: UITableViewController { enum AppIconRows: Int, CaseIterable { case modern case classic case ny2026 public func getName() -> String { switch self { case .modern: return "Modern" case .classic: return "Classic" case .ny2026: return "Neo" } } var description : String { switch self { case .modern: return "modern" case .classic: return "classic-2025" case .ny2026: return "ny-2026" } } static let count: Int = { var max: Int = 0 while let _ = AppIconRows(rawValue: max) { max += 1 } return max }() } override func viewDidLoad() { self.title = NSLocalizedString("Icon", comment: "Settings") super.viewDidLoad() } @objc func cancel() { self.navigationController?.popViewController(animated: true) } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { for row in AppIconRows.allCases { if let cell = tableView.cellForRow(at: IndexPath(row: row.rawValue, section: 0)) { cell.accessoryType = .none } } if let cell = tableView.cellForRow(at: indexPath) { cell.accessoryType = .checkmark if let icon = AppIconRows(rawValue: indexPath.row)?.description { let name = icon == "modern" ? nil : icon UIApplication.shared.setAlternateIconName(name) { error in if let error = error { print("Error setting alternate icon \(String(describing: name)): \(error.localizedDescription)") } else { UserDefaultsManagement.appIcon = indexPath.row } } } } tableView.deselectRow(at: indexPath, animated: false) } override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 120 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell() let marginguide = cell.contentView.layoutMarginsGuide cell.imageView?.translatesAutoresizingMaskIntoConstraints = false cell.imageView?.topAnchor.constraint(equalTo: marginguide.topAnchor).isActive = true cell.imageView?.leadingAnchor.constraint(equalTo: marginguide.leadingAnchor).isActive = true cell.imageView?.heightAnchor.constraint(equalToConstant: 100).isActive = true cell.imageView?.widthAnchor.constraint(equalToConstant: 100).isActive = true cell.imageView?.layer.borderColor = UIColor.gray.cgColor cell.imageView?.layer.borderWidth = 2 cell.imageView?.layer.backgroundColor = UIColor.white.cgColor cell.imageView?.contentMode = .scaleAspectFill cell.imageView?.layer.cornerRadius = 20 if let icon = AppIconRows(rawValue: indexPath.row) { let iconName = "AppIcon" + icon.description.capitalizingFirstLetter() if let image = UIImage(named: iconName) { cell.imageView?.image = image } cell.textLabel?.text = icon.getName() } return cell } override func numberOfSections(in tableView: UITableView) -> Int { return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return AppIconRows.count } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if indexPath.row == UserDefaultsManagement.appIcon { cell.accessoryType = .checkmark } } } ================================================ FILE: FSNotes iOS/Preferences/CodeFontViewController.swift ================================================ // // CodeFontViewController.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 3/16/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit class CodeFontViewController: UITableViewController { private var fontFamilyNames = [ "Source Code Pro", "Menlo", "Courier", ] override func viewDidLoad() { title = NSLocalizedString("Font Family", comment: "Settings") super.viewDidLoad() } @objc func cancel() { self.navigationController?.popViewController(animated: true) } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if fontFamilyNames[indexPath.row] == UserDefaultsManagement.codeFont.familyName { cell.accessoryType = .checkmark } } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let cell = tableView.cellForRow(at: indexPath), let label = cell.textLabel, let fontFamily = label.text { let fontSize = UserDefaultsManagement.fontSize if indexPath.row == 0 { UserDefaultsManagement.codeFontName = "Source Code Pro" } else if let customFont = UIFont(name: fontFamily, size: CGFloat(fontSize)) { UserDefaultsManagement.codeFont = customFont } MPreviewView.template = nil UIApplication.getVC().notesTable.reloadData() self.navigationController?.popViewController(animated: true) } } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell() cell.textLabel?.text = fontFamilyNames[indexPath.row] return cell } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return fontFamilyNames.count } } ================================================ FILE: FSNotes iOS/Preferences/CodeThemeViewController.swift ================================================ // // CodeThemeViewController.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 3/16/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit class CodeThemeViewController: UITableViewController { private var themeNames = [ "github", "solarized", "atom-one" ] override func viewDidLoad() { title = NSLocalizedString("Code Theme", comment: "Settings") super.viewDidLoad() } @objc func cancel() { self.navigationController?.popViewController(animated: true) } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if themeNames[indexPath.row] == UserDefaultsManagement.codeTheme.getName() { cell.accessoryType = .checkmark } } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let cell = tableView.cellForRow(at: indexPath), let label = cell.textLabel, let theme = label.text { if let theme = EditorTheme(themeName: theme) { UserDefaultsManagement.codeTheme = theme } NotesTextProcessor.hl = nil MPreviewView.template = nil UIApplication.getVC().notesTable.reloadData() self.navigationController?.popViewController(animated: true) } } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell() cell.textLabel?.text = themeNames[indexPath.row] return cell } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return themeNames.count } } ================================================ FILE: FSNotes iOS/Preferences/DefaultExtensionControllerView.swift ================================================ // // DefaultExtensionControllerView.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 2/28/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit class DefaultExtensionViewController: UITableViewController { private var sections = [ NSLocalizedString("Container", comment: "Settings"), NSLocalizedString("Extension", comment: "Settings"), NSLocalizedString("Files Naming", comment: "Settings"), ] private var rowsInSection = [1, 3, 5] private var extensions = ["markdown", "md", "txt"] private var naming = [ NSLocalizedString("Autoname By Title", comment: "Settings"), NSLocalizedString("Auto Rename By Title", comment: "Settings"), NSLocalizedString("Format: Untitled Note", comment: "Settings"), NSLocalizedString("Format: yyyyMMddHHmmss", comment: "Settings"), NSLocalizedString("Format: yyyy-MM-dd hh.mm.ss a", comment: "Settings"), ] override func viewDidLoad() { self.title = NSLocalizedString("Files", comment: "Settings") } @objc func cancel() { self.navigationController?.popViewController(animated: true) } override func numberOfSections(in tableView: UITableView) -> Int { return sections.count } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return rowsInSection[section] } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return sections[section] } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 50 } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let cell = tableView.cellForRow(at: indexPath), let label = cell.textLabel, let ext = label.text { if indexPath.section == 1 { UserDefaultsManagement.noteExtension = ext UserDefaultsManagement.fileFormat = NoteType.withExt(rawValue: ext) for index in 0...rowsInSection[indexPath.section] { let indexPath = IndexPath(row: index, section: 1) if let cell = tableView.cellForRow(at: indexPath) { cell.accessoryType = .none tableView.deselectRow(at: indexPath, animated: false) } } cell.accessoryType = .checkmark } else if indexPath.section == 2 { for index in 0...rowsInSection[indexPath.section] { let indexPath = IndexPath(row: index, section: 2) if let cell = tableView.cellForRow(at: indexPath) { cell.accessoryType = .none tableView.deselectRow(at: indexPath, animated: false) } } if let id = SettingsFilesNaming(rawValue: cell.tag) { UserDefaultsManagement.naming = id cell.accessoryType = .checkmark } } } } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { guard let text = cell.textLabel?.text else { return } if indexPath.section == 1 { if UserDefaultsManagement.noteExtension == text { cell.accessoryType = .checkmark } else { cell.accessoryType = .none } } else if indexPath.section == 2 { if UserDefaultsManagement.naming == SettingsFilesNaming(rawValue: cell.tag) { cell.accessoryType = .checkmark } else { cell.accessoryType = .none } } } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell() if indexPath.section == 0 { let uiSwitch = UISwitch() uiSwitch.addTarget(self, action: #selector(switchValueDidChange(_:)), for: .valueChanged) uiSwitch.isOn = UserDefaultsManagement.fileContainer == .textBundle || UserDefaultsManagement.fileContainer == .textBundleV2 cell.textLabel?.text = "Textbundle" cell.accessoryView = uiSwitch } else if indexPath.section == 1 { cell.textLabel?.text = extensions[indexPath.row] } else if indexPath.section == 2 { if indexPath.row == 0 { cell.textLabel?.text = naming[0] cell.tag = 5 } else { cell.textLabel?.text = naming[indexPath.row] cell.tag = indexPath.row } } return cell } @objc public func switchValueDidChange(_ sender: UISwitch) { guard let cell = sender.superview as? UITableViewCell else { return } guard let uiSwitch = cell.accessoryView as? UISwitch else { return } UserDefaultsManagement.fileContainer = uiSwitch.isOn ? .textBundleV2 : .none } } ================================================ FILE: FSNotes iOS/Preferences/ExternalViewController.swift ================================================ // // ExternalViewController.swift // FSNotes iOS // // Created by Александр on 20.02.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // import Foundation import UIKit class ExternalViewController: UIDocumentPickerViewController, UIDocumentPickerDelegate { func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { guard urls.count == 1, let url = urls.first, url.hasDirectoryPath else { return } guard url.startAccessingSecurityScopedResource() else { return } do { let bookmarkData = try url.bookmarkData(options: .minimalBookmark, includingResourceValuesForKeys: nil, relativeTo: nil) SandboxBookmark.sharedInstance().save(data: bookmarkData) let storage = Storage.shared() if storage.projectExist(url: url) { return } if let projects = Storage.shared().insert(url: url, bookmark: true) { OperationQueue.main.addOperation { UIApplication.getVC().sidebarTableView.insertRows(projects: projects) _ = UIApplication.getNC()?.popViewController(animated: true) if !UserDefaultsManagement.sidebarIsOpened { UIApplication.getVC().openSidebar() } if let project = projects.first { UIApplication.getVC().sidebarTableView.select(project: project) } } } } catch { print(error) } } } ================================================ FILE: FSNotes iOS/Preferences/FontViewController.swift ================================================ // // FontViewController.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 3/16/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit class FontViewController: UITableViewController { private var fontFamilyNames = [ "System", "Avenir Next", "Georgia", "Helvetica Neue", "Menlo", "Courier", "Palatino" ] override func viewDidLoad() { title = NSLocalizedString("Font Family", comment: "Settings") super.viewDidLoad() } @objc func cancel() { self.navigationController?.popViewController(animated: true) } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if UserDefaultsManagement.fontName == nil && indexPath.row == 0 { cell.accessoryType = .checkmark } if fontFamilyNames[indexPath.row] == UserDefaultsManagement.noteFont.familyName { cell.accessoryType = .checkmark } } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let cell = tableView.cellForRow(at: indexPath), let label = cell.textLabel, let fontFamily = label.text { let fontSize = UserDefaultsManagement.fontSize if indexPath.row == 0 { UserDefaultsManagement.fontName = nil } else if let customFont = UIFont(name: fontFamily, size: CGFloat(fontSize)) { UserDefaultsManagement.noteFont = customFont } MPreviewView.template = nil UIApplication.getVC().notesTable.reloadData() self.navigationController?.popViewController(animated: true) } } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell() cell.textLabel?.text = fontFamilyNames[indexPath.row] return cell } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return fontFamilyNames.count } } ================================================ FILE: FSNotes iOS/Preferences/GitTableViewCell.swift ================================================ // // GitTableCellView.swift // FSNotes iOS // // Created by Oleksandr Hlushchenko on 01.03.2023. // Copyright © 2023 Oleksandr Hlushchenko. All rights reserved. // import Foundation import UIKit class GitTableViewCell: UITableViewCell { public var project: Project? @IBOutlet weak var removeButton: UIButton! @IBOutlet weak var cloneButton: UIButton! @IBOutlet weak var activity: UIActivityIndicatorView! } ================================================ FILE: FSNotes iOS/Preferences/GitViewController.swift ================================================ // // GitViewController.swift // FSNotes iOS // // Created by Oleksandr Hlushchenko on 05.02.2023. // Copyright © 2023 Oleksandr Hlushchenko. All rights reserved. // import UIKit import CoreServices class GitViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { @IBOutlet weak var tableView: UITableView! enum GitSection: Int, CaseIterable { case automation case credentials case origin case logs var title: String { switch self { case .automation: return "Automation" case .credentials: return "Credentials" case .origin: return "Origin" case .logs: return "Status" } } } private var hasActiveGit: Bool = false private var progress: GitProgress? private var project: Project? public var activity: UIActivityIndicatorView? public var leftButton: UIButton? public var rightButton: UIButton? public var logTextField: UITextField? public func setProject(_ project: Project) { self.project = project } override func viewDidLoad() { self.title = NSLocalizedString("Git", comment: "Settings") navigationItem.largeTitleDisplayMode = .always tableView.delegate = self tableView.dataSource = self super.viewDidLoad() setupKeyboardObservers() tableView.keyboardDismissMode = .interactive } override func viewWillAppear(_ animated: Bool) { UIApplication.shared.isIdleTimerDisabled = true DispatchQueue.main.async { self.updateButtons(isActive: self.hasActiveGit) if let status = self.project?.gitStatus { self.logTextField?.text = status } } } override func viewWillDisappear(_ animated: Bool) { UIApplication.shared.isIdleTimerDisabled = false } func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return GitSection(rawValue: section)?.title } @objc func cancel() { self.navigationController?.popViewController(animated: true) } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if indexPath.section == GitSection.credentials.rawValue && indexPath.row == 0 { changePrivateKey(tableView: tableView, indexPath: indexPath) } if indexPath.section == GitSection.credentials.rawValue && indexPath.row == 1 { changePublicKey(tableView: tableView, indexPath: indexPath) } tableView.deselectRow(at: indexPath, animated: false) } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 50 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let project = project else { return UITableViewCell() } if indexPath.section == GitSection.automation.rawValue { let cell = UITableViewCell(style: .value1, reuseIdentifier: nil) if indexPath.row == 0 { cell.textLabel?.text = NSLocalizedString("Pull (every 30 sec)", comment: "") let uiSwitch = UISwitch() uiSwitch.addTarget(self, action: #selector(autoPullDidChange(_:)), for: .valueChanged) uiSwitch.isOn = project.settings.gitAutoPull cell.accessoryView = uiSwitch } return cell } // Passphrase and origin textfields if indexPath.section == GitSection.credentials.rawValue && indexPath.row == 2 || ( indexPath.section == GitSection.origin.rawValue && indexPath.row == 0 || indexPath.section == GitSection.logs.rawValue && indexPath.row == 0 ) { let cell = UITableViewCell(style: .value1, reuseIdentifier: nil) let textField = UITextField() textField.textColor = UIColor.blackWhite // Passphrase if indexPath.section == GitSection.credentials.rawValue && indexPath.row == 2 { cell.textLabel?.text = NSLocalizedString("Passphrase", comment: "") textField.isSecureTextEntry = true textField.addTarget(self, action: #selector(passphraseDidChange), for: .editingChanged) textField.placeholder = "(optional)" textField.text = project.settings.gitPrivateKeyPassphrase } // Origin if indexPath.section == GitSection.origin.rawValue && indexPath.row == 0 { textField.addTarget(self, action: #selector(originDidChange), for: .editingChanged) textField.placeholder = "git@github.com:username/example.git" textField.text = project.settings.gitOrigin ?? "" } // Logs if indexPath.section == GitSection.logs.rawValue && indexPath.row == 0 { textField.placeholder = "no data" textField.isEnabled = false logTextField = textField progress = GitProgress(statusTextField: textField, project: project) // Global instance AppDelegate.gitProgress = progress } textField.translatesAutoresizingMaskIntoConstraints = false textField.textAlignment = .right cell.contentView.addSubview(textField) cell.addConstraint(NSLayoutConstraint(item: textField, attribute: .leading, relatedBy: .equal, toItem: cell.textLabel, attribute: .trailing, multiplier: 1, constant: 8)) cell.addConstraint(NSLayoutConstraint(item: textField, attribute: .top, relatedBy: .equal, toItem: cell.contentView, attribute: .top, multiplier: 1, constant: 8)) cell.addConstraint(NSLayoutConstraint(item: textField, attribute: .bottom, relatedBy: .equal, toItem: cell.contentView, attribute: .bottom, multiplier: 1, constant: -8)) cell.addConstraint(NSLayoutConstraint(item: textField, attribute: .trailing, relatedBy: .equal, toItem: cell.contentView, attribute: .trailing, multiplier: 1, constant: -8)) return cell } // Clone button if indexPath.section == GitSection.origin.rawValue && indexPath.row == 1 { let cell = tableView.dequeueReusableCell(withIdentifier: "gitTableViewCell", for: indexPath) as! GitTableViewCell cell.selectionStyle = .none cell.cloneButton.addTarget(self, action: #selector(repoPressed), for: .touchUpInside) cell.removeButton.addTarget(self, action: #selector(removePressed), for: .touchUpInside) leftButton = cell.cloneButton rightButton = cell.removeButton activity = cell.activity activity?.isHidden = true activity?.startAnimating() return cell } let cell = UITableViewCell(style: .value1, reuseIdentifier: nil) // Private key if indexPath.section == GitSection.credentials.rawValue && indexPath.row == 0 { cell.textLabel?.text = NSLocalizedString("Private Key", comment: "") cell.detailTextLabel?.text = NSLocalizedString("...", comment: "") if project.settings.gitPrivateKey != nil { cell.detailTextLabel?.text = NSLocalizedString("✅ - ", comment: "") let accessoryButton = UIButton(type: .system) accessoryButton.addTarget(self, action: #selector(deletePrivateKey(sender:)), for: .touchUpInside) accessoryButton.setImage(UIImage(systemName: "trash"), for: .normal) accessoryButton.frame = CGRect(x: 0, y: 0, width: 35, height: 35) cell.accessoryView = accessoryButton } } // Public key if indexPath.section == GitSection.credentials.rawValue && indexPath.row == 1 { cell.textLabel?.text = NSLocalizedString("Public Key (optional)", comment: "") cell.detailTextLabel?.text = NSLocalizedString("...", comment: "") if project.settings.gitPublicKey != nil { cell.detailTextLabel?.text = NSLocalizedString("✅ - ", comment: "") let accessoryButton = UIButton(type: .system) accessoryButton.addTarget(self, action: #selector(deletePublicKey(sender:)), for: .touchUpInside) accessoryButton.setImage(UIImage(systemName: "trash"), for: .normal) accessoryButton.frame = CGRect(x: 0, y: 0, width: 35, height: 35) cell.accessoryView = accessoryButton } } return cell } func numberOfSections(in tableView: UITableView) -> Int { return GitSection.allCases.count } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if section == 0 { return 1 } if section == 1 { return 3 } if section == 2 { return 2 } return 1 } private lazy var documentPickerPrivateKey: UIDocumentPickerViewController = { let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.data], asCopy: true) documentPicker.delegate = self documentPicker.allowsMultipleSelection = false documentPicker.modalPresentationStyle = .formSheet return documentPicker }() private lazy var documentPickerPublicKey: UIDocumentPickerViewController = { let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.data], asCopy: true) documentPicker.delegate = self documentPicker.allowsMultipleSelection = false documentPicker.modalPresentationStyle = .formSheet return documentPicker }() private func changePrivateKey(tableView: UITableView, indexPath: IndexPath) { present(documentPickerPrivateKey, animated: true, completion: nil) } private func changePublicKey(tableView: UITableView, indexPath: IndexPath) { present(documentPickerPublicKey, animated: true, completion: nil) } @objc func deletePrivateKey(sender: UIButton) { guard let project = project else { return } project.settings.gitPrivateKey = nil project.saveSettings() if let privateUrl = project.getSSHKeyUrl(), FileManager.default.fileExists(atPath: privateUrl.path) { try? FileManager.default.removeItem(at: privateUrl) } guard let cell = sender.superview as? UITableViewCell, let tableView = cell.superview as? UITableView, let indexPath = tableView.indexPath(for: cell) else { return } tableView.reloadRows(at: [indexPath], with: .none) } @objc func deletePublicKey(sender: UIButton) { guard let project = project else { return } project.settings.gitPublicKey = nil project.saveSettings() if let pubUrl = project.getSSHKeyUrl()?.appendingPathExtension("pub"), FileManager.default.fileExists(atPath: pubUrl.path) { try? FileManager.default.removeItem(at: pubUrl) } guard let cell = sender.superview as? UITableViewCell, let tableView = cell.superview as? UITableView, let indexPath = tableView.indexPath(for: cell) else { return } tableView.reloadRows(at: [indexPath], with: .none) } @objc func passphraseDidChange(sender: UITextField) { guard let text = sender.text else { return } guard let project = project else { return } project.settings.gitPrivateKeyPassphrase = text project.saveSettings() } @objc func originDidChange(sender: UITextField) { guard let project = project, let origin = sender.text else { return } project.settings.setOrigin(origin) project.saveSettings() updateButtons() } @objc func removePressed(sender: UIButton) { guard let project = project else { return } project.removeSSHKey() project.removeRepository() rightButton?.isEnabled = false progress?.log(message: "git repository removed") updateButtons() } @objc func repoPressed(sender: UIButton) { guard let project = project else { return } let action = project.getRepositoryState() updateButtons(isActive: true) UIApplication.shared.isIdleTimerDisabled = true UIApplication.getVC().gitQueue.addOperation({ defer { DispatchQueue.main.async { UIApplication.shared.isIdleTimerDisabled = false UIApplication.getVC().scheduledGitPull() self.updateButtons(isActive: false) } } if let message = project.gitDo(action, progress: self.progress) { DispatchQueue.main.async { self.errorAlert(title: "git error", message: message) // Refresh local files if action == .pullPush && !UserDefaultsManagement.iCloudDrive { UIApplication.getVC().checkNew() } } } }) } public func errorAlert(title: String, message: String) { DispatchQueue.main.async { let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) let okAction = UIAlertAction(title: "OK", style: .cancel) { (_) in } alertController.addAction(okAction) self.present(alertController, animated: true, completion: nil) } } public func updateButtons(isActive: Bool? = nil) { guard let project = project else { return } if let isActive = isActive { hasActiveGit = isActive leftButton?.isEnabled = !isActive activity?.isHidden = !isActive } rightButton?.isEnabled = project.hasRepository() let state = project.getRepositoryState() leftButton?.setTitle(state.title, for: .normal) } @objc public func autoPullDidChange(_ sender: UISwitch) { guard let cell = sender.superview as? UITableViewCell else { return } guard let uiSwitch = cell.accessoryView as? UISwitch else { return } guard let project = project else { return } project.settings.gitAutoPull = uiSwitch.isOn project.saveSettings() } public func setProgress(message: String) { progress?.log(message: message) } private func setupKeyboardObservers() { NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil ) } @objc private func keyboardWillShow(_ notification: Notification) { guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } let keyboardHeight = keyboardFrame.height let bottomSafeArea = view.safeAreaInsets.bottom UIView.animate(withDuration: 0.3) { self.tableView.contentInset.bottom = keyboardHeight - bottomSafeArea self.tableView.verticalScrollIndicatorInsets.bottom = keyboardHeight - bottomSafeArea } } @objc private func keyboardWillHide(_ notification: Notification) { UIView.animate(withDuration: 0.3) { self.tableView.contentInset.bottom = 0 self.tableView.verticalScrollIndicatorInsets.bottom = 0 } } deinit { NotificationCenter.default.removeObserver(self) } } extension GitViewController: UIDocumentPickerDelegate, UINavigationControllerDelegate { func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { guard let url = urls.first else { return } guard let data = try? Data(contentsOf: url) else { return } guard let project = project else { return } if controller == documentPickerPrivateKey { project.settings.gitPrivateKey = data } if controller == documentPickerPublicKey { project.settings.gitPublicKey = data } project.saveSettings() tableView.reloadData() } func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { controller.dismiss(animated: true, completion: nil) } } ================================================ FILE: FSNotes iOS/Preferences/LanguageViewController.swift ================================================ // // LanguageViewController.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 3/12/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit class LanguageViewController: UITableViewController { private var languages: [String]? = [] override func viewDidLoad() { for im in UITextInputMode.activeInputModes { if let lang = im.primaryLanguage { self.languages?.append(lang) } } self.title = NSLocalizedString("Default Keyboard", comment: "Settings") super.viewDidLoad() } @objc func cancel() { self.navigationController?.popViewController(animated: true) } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { UserDefaultsManagement.defaultKeyboard = languages?[ indexPath.row] self.navigationController?.popViewController(animated: true) } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell() cell.textLabel?.text = languages?[indexPath.row] return cell } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if let l = languages { return l.count } return 0 } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { guard let language = UserDefaultsManagement.defaultKeyboard else { return } if languages?[indexPath.row] == language { cell.accessoryType = .checkmark } } } ================================================ FILE: FSNotes iOS/Preferences/ProViewController.swift ================================================ // // ProViewController.swift // FSNotes iOS // // Created by Александр on 19.02.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // import UIKit class ProViewController: UITableViewController { private var sections = [ NSLocalizedString("+", comment: "Settings"), NSLocalizedString("View", comment: "Settings"), ] override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return sections[section] } private var rows = [ [ NSLocalizedString("Default Keyboard", comment: ""), NSLocalizedString("Use Inline Tags", comment: ""), NSLocalizedString("Use TextBundle info.json to store c/mtime", comment: "") ], [ NSLocalizedString("Sort By", comment: ""), NSLocalizedString("Library", comment: "") ] ] override func viewDidLoad() { self.title = NSLocalizedString("Advanced", comment: "Settings") super.viewDidLoad() } @objc func cancel() { self.navigationController?.popViewController(animated: true) } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if indexPath.section == 1 { if indexPath.row == 0 { self.navigationController?.pushViewController(SortByViewController(), animated: true) } else { self.navigationController?.pushViewController(SidebarViewController(), animated: true) } } if indexPath.section == 0, indexPath.row == 0 { self.navigationController?.pushViewController(LanguageViewController(), animated: true) } tableView.deselectRow(at: indexPath, animated: false) } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let uiSwitch = UISwitch() uiSwitch.addTarget(self, action: #selector(switchValueDidChange(_:)), for: .valueChanged) let cell = UITableViewCell() cell.textLabel?.text = rows[indexPath.section][indexPath.row] if indexPath.section == 0 { switch indexPath.row { case 1: cell.accessoryView = uiSwitch uiSwitch.isOn = UserDefaultsManagement.inlineTags break case 2: cell.accessoryView = uiSwitch uiSwitch.isOn = UserDefaultsManagement.useTextBundleMetaToStoreDates break default: break } } if indexPath.section == 1 { cell.accessoryType = .disclosureIndicator } return cell } override func numberOfSections(in tableView: UITableView) -> Int { return rows.count } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return rows[section].count } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if indexPath.row == 0 { cell.accessoryType = .disclosureIndicator } } @objc public func switchValueDidChange(_ sender: UISwitch) { guard let cell = sender.superview as? UITableViewCell, let tableView = cell.superview as? UITableView, let indexPath = tableView.indexPath(for: cell) else { return } switch indexPath.row { case 1: guard let uiSwitch = cell.accessoryView as? UISwitch else { return } UserDefaultsManagement.inlineTags = uiSwitch.isOn let vc = UIApplication.getVC() if UserDefaultsManagement.inlineTags { vc.sidebarTableView.loadAllTags() } else { vc.sidebarTableView.unloadAllTags() } vc.resizeSidebar(withAnimation: true) case 2: guard let uiSwitch = cell.accessoryView as? UISwitch else { return } UserDefaultsManagement.useTextBundleMetaToStoreDates = uiSwitch.isOn default: return } } private func autoVersioningPrompt() { let title = NSLocalizedString("Сlearing history", comment: "") let message = NSLocalizedString("Are you sure you want to delete the history of all notes?", comment: "") let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default) { (_) in let revisions = Storage.shared().getRevisionsHistoryDocumentsSupport() do { try FileManager.default.removeItem(at: revisions) } catch { print("History clear: \(error)") } self.dismiss(animated: true) }) let cancel = NSLocalizedString("Cancel", comment: "") alert.addAction(UIAlertAction(title: cancel, style: .cancel, handler: { (action: UIAlertAction!) in })) self.present(alert, animated: true, completion: nil) } } ================================================ FILE: FSNotes iOS/Preferences/ProjectSettingsViewController.swift ================================================ // // ProjectSettingsViewController.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 9/20/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit class ProjectSettingsViewController: UITableViewController { private var dismiss: Bool = false private var project: Project private var sections = [ NSLocalizedString("Sort By", comment: ""), NSLocalizedString("Sort Direction", comment: ""), NSLocalizedString("Visibility", comment: ""), NSLocalizedString("Notes List", comment: "") ] private var rowsInSections = [4, 2, 2, 1] init(project: Project, dismiss: Bool = false) { self.project = project self.dismiss = dismiss super.init(style: .grouped) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { if dismiss { self.navigationItem.rightBarButtonItem = Buttons.getDone(target: self, selector: #selector(close)) } self.title = project.getFullLabel() super.viewDidLoad() } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let vc = UIApplication.getVC() if let cell = tableView.cellForRow(at: indexPath) { if indexPath.section == 0x00 { for row in 0...rowsInSections[indexPath.section] { let cell = tableView.cellForRow(at: IndexPath(row: row, section: indexPath.section)) cell?.accessoryType = .none } if let sort = SortBy(rawValue: cell.reuseIdentifier!) { self.project.settings.sortBy = sort vc.buildSearchQuery() vc.reloadNotesTable() } if cell.accessoryType == .none { cell.accessoryType = .checkmark } else { cell.accessoryType = .none } } if indexPath.section == 0x01 { for row in 0...rowsInSections[indexPath.section] { let cell = tableView.cellForRow(at: IndexPath(row: row, section: indexPath.section)) cell?.accessoryType = .none } if let sort = SortDirection(rawValue: cell.reuseIdentifier!) { self.project.settings.sortDirection = sort vc.buildSearchQuery() vc.reloadNotesTable() } if cell.accessoryType == .none { cell.accessoryType = .checkmark } else { cell.accessoryType = .none } } } project.saveSettings() } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return rowsInSections[section] } override func numberOfSections(in tableView: UITableView) -> Int { return 4 } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 50 } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return sections[section] } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let uiSwitch = UISwitch() uiSwitch.addTarget(self, action: #selector(switchValueDidChange(_:)), for: .valueChanged) var cell = UITableViewCell() if indexPath.section == 0x00 { switch indexPath.row { case 0: cell = UITableViewCell(style: .default, reuseIdentifier: "none") cell.textLabel?.text = NSLocalizedString("None", comment: "") if project.settings.sortBy.rawValue == "none" { cell.accessoryType = .checkmark } break case 1: cell = UITableViewCell(style: .default, reuseIdentifier: "modificationDate") cell.textLabel?.text = NSLocalizedString("Modification Date", comment: "") if project.settings.sortBy.rawValue == "modificationDate" { cell.accessoryType = .checkmark } break case 2: cell = UITableViewCell(style: .default, reuseIdentifier: "creationDate") cell.textLabel?.text = NSLocalizedString("Creation Date", comment: "") if project.settings.sortBy.rawValue == "creationDate" { cell.accessoryType = .checkmark } break case 3: cell = UITableViewCell(style: .default, reuseIdentifier: "title") cell.textLabel?.text = NSLocalizedString("Title", comment: "") if project.settings.sortBy.rawValue == "title" { cell.accessoryType = .checkmark } break default: break } } if indexPath.section == 0x01 { switch indexPath.row { case 0: cell = UITableViewCell(style: .default, reuseIdentifier: "asc") cell.textLabel?.text = NSLocalizedString("Ascending", comment: "") if project.settings.sortDirection.rawValue == "asc" { cell.accessoryType = .checkmark } break case 1: cell = UITableViewCell(style: .default, reuseIdentifier: "desc") cell.textLabel?.text = NSLocalizedString("Descending", comment: "") if project.settings.sortDirection.rawValue == "desc" { cell.accessoryType = .checkmark } break default: break } } if indexPath.section == 0x02 { switch indexPath.row { case 0: cell.accessoryView = uiSwitch uiSwitch.isOn = project.settings.showInCommon uiSwitch.isEnabled = !project.isDefault && !project.isTrash && !project.isVirtual cell.textLabel?.text = NSLocalizedString("Show Notes in \"Notes\" and \"Todo\"", comment: "") case 1: cell.accessoryView = uiSwitch uiSwitch.isOn = project.settings.showInSidebar uiSwitch.isEnabled = !project.isDefault && !project.isTrash && !project.isVirtual cell.textLabel?.text = NSLocalizedString("Show Folder in Library", comment: "") default: return cell } } if indexPath.section == 0x03 { cell.accessoryView = uiSwitch uiSwitch.isOn = project.settings.isFirstLineAsTitle() uiSwitch.isEnabled = !project.isVirtual cell.textLabel?.text = NSLocalizedString("Use First Line as Title", comment: "") } return cell } @objc public func switchValueDidChange(_ sender: UISwitch) { guard let cell = sender.superview as? UITableViewCell, let tableView = cell.superview as? UITableView, let indexPath = tableView.indexPath(for: cell) else { return } let vc = UIApplication.getVC() if indexPath.section == 0x02 { if indexPath.row == 0x00 { guard let uiSwitch = cell.accessoryView as? UISwitch else { return } self.project.settings.showInCommon = uiSwitch.isOn vc.reloadNotesTable() } else { guard let uiSwitch = cell.accessoryView as? UISwitch else { return } self.project.settings.showInSidebar = uiSwitch.isOn OperationQueue.main.addOperation { if !uiSwitch.isOn { let at = IndexPath(row: 0, section: 0) vc.sidebarTableView.tableView(vc.sidebarTableView, didSelectRowAt: at) vc.sidebarTableView.removeRows(projects: [self.project]) } else { vc.sidebarTableView.insertRows(projects: [self.project]) } } } } else if indexPath.section == 0x03 { guard let uiSwitch = cell.accessoryView as? UISwitch else { return } project.settings.firstLineAsTitle = uiSwitch.isOn let notes = Storage.shared().getNotesBy(project: project) for note in notes { note.invalidateCache() } vc.reloadNotesTable() } project.saveSettings() } @objc func cancel() { navigationController?.popViewController(animated: true) } @objc func close() { dismiss(animated: true, completion: nil) } } ================================================ FILE: FSNotes iOS/Preferences/ProjectsViewController.swift ================================================ // // ProjectsViewController.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 9/20/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit import CoreServices import UniformTypeIdentifiers class ProjectsViewController: UITableViewController, UIDocumentPickerDelegate { private var projects: [Project] init() { let storage = Storage.shared() self.projects = storage.getProjects() super.init(style: .plain) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { let addProject = Buttons.getAdd(target: self, selector: #selector(newAlert)) var buttons = [UIBarButtonItem]() buttons.append(addProject) // if #available(iOS 13.0, *) { // let external = Buttons.getAttach(target: self, selector: #selector(attachExternal)) // // buttons.append(external) // } self.navigationItem.rightBarButtonItems = buttons self.projects = Storage.shared().getProjects() self.title = NSLocalizedString("Folders", comment: "Settings") super.viewDidLoad() } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let project = self.projects[indexPath.row] let controller = ProjectSettingsViewController(project: project) self.navigationController?.pushViewController(controller, animated: true) } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell() if self.projects.count > 0 { let project = projects[indexPath.row] if project.isTrash { cell.textLabel?.text = NSLocalizedString("Trash", comment: "") } else { cell.textLabel?.text = project.getNestedLabel() } } cell.accessoryType = .disclosureIndicator return cell } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.projects.count } override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle { return .none } override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { let project = self.projects[indexPath.row] if project.isDefault { return nil } if project.isTrash { return nil } let deleteAction = UIContextualAction(style: .destructive, title: NSLocalizedString("Delete", comment: "")) { (action, view, completionHandler) in self.delete(project: project) completionHandler(true) } deleteAction.backgroundColor = UIColor(red:0.93, green:0.31, blue:0.43, alpha:1.0) let configuration = UISwipeActionsConfiguration(actions: [deleteAction]) configuration.performsFirstActionWithFullSwipe = true // This mimics the full swipe behavior if needed return configuration } @objc func cancel() { self.navigationController?.popViewController(animated: true) } @objc func newAlert() { let alertController = UIAlertController(title: NSLocalizedString("Folder name:", comment: ""), message: nil, preferredStyle: .alert) alertController.addTextField { (textField) in textField.placeholder = "" } let confirmAction = UIAlertAction(title: "OK", style: .default) { (_) in guard let name = alertController.textFields?[0].text, name.count > 0 else { return } guard self.projects.first(where: { $0.label == name } ) == nil else { let alert = UIAlertController(title: "Oops 👮‍♂️", message: NSLocalizedString("Folder with this name already exist", comment: ""), preferredStyle: UIAlertController.Style.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)) self.present(alert, animated: true, completion: nil) return } guard let newDir = UserDefaultsManagement.storageUrl?.appendingPathComponent(name, isDirectory: true) else { return } do { try FileManager.default.createDirectory(at: newDir, withIntermediateDirectories: false, attributes: nil) } catch { print(error) return } if let projects = Storage.shared().insert(url: newDir) { self.tableView.reloadData() OperationQueue.main.addOperation { UIApplication.getVC().sidebarTableView.insertRows(projects: projects) } } } let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) { (_) in } alertController.addAction(confirmAction) alertController.addAction(cancelAction) self.present(alertController, animated: true, completion: nil) } @objc func attachExternal() { let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.folder]) documentPicker.delegate = self present(documentPicker, animated: true, completion: nil) } private func delete(project: Project) { if project.isBookmark { self.removeProject(project: project) SandboxBookmark.sharedInstance().remove(url: project.url) return } let message = "Are you sure you want to remove project \"\(project.getFullLabel())\" and all files inside?" let alertController = UIAlertController(title: NSLocalizedString("Project removing ❌", comment: ""), message: message, preferredStyle: .alert) let confirmAction = UIAlertAction(title: "OK", style: .default) { (_) in project.remove() self.removeProject(project: project) } let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) { (_) in } alertController.addAction(confirmAction) alertController.addAction(cancelAction) self.present(alertController, animated: true, completion: nil) } private func removeProject(project: Project) { if let i = self.projects.firstIndex(of: project) { self.projects.remove(at: i) } self.tableView.reloadData() Storage.shared().removeBy(project: project) let vc = UIApplication.getVC() vc.reloadNotesTable() { OperationQueue.main.addOperation { vc.sidebarTableView.removeRows(projects: [project]) } } } func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { guard urls.count == 1, let url = urls.first, url.hasDirectoryPath else { return } guard url.startAccessingSecurityScopedResource() else { return } do { let bookmarkData = try url.bookmarkData(options: .minimalBookmark, includingResourceValuesForKeys: nil, relativeTo: nil) SandboxBookmark.sharedInstance().save(data: bookmarkData) if let projects = Storage.shared().insert(url: url) { OperationQueue.main.addOperation { UIApplication.getVC().sidebarTableView.insertRows(projects: projects) self.projects.append(contentsOf: projects) self.tableView.reloadData() } } } catch { print(error) } } } ================================================ FILE: FSNotes iOS/Preferences/SecurityViewController.swift ================================================ // // SecurityViewController.swift // FSNotes iOS // // Created by Oleksandr Hlushchenko on 08.04.2023. // Copyright © 2023 Oleksandr Hlushchenko. All rights reserved. // import UIKit class SecurityViewController: UITableViewController { lazy var saveButton: UIButton = { let button : UIButton = UIButton(type: UIButton.ButtonType.custom) as UIButton button.backgroundColor = UIColor.systemBlue button.layer.cornerRadius = 10 button.addTarget(self, action: #selector(saveButtonClicked), for: .touchUpInside) button.setTitle(NSLocalizedString("Save", comment: ""), for: UIControl.State.normal) button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 25, bottom: 10, right: 25) return button }() var passwordTextField: UITextField? var verifyPasswordTextField: UITextField? override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return NSLocalizedString("Master", comment: "Settings") } override func viewDidLoad() { self.title = NSLocalizedString("Security", comment: "Settings") super.viewDidLoad() } @objc func cancel() { self.navigationController?.popViewController(animated: true) } override func numberOfSections(in tableView: UITableView) -> Int { return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 3 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell(style: .value1, reuseIdentifier: nil) let textField = UITextField() var password = String() do { let item = KeychainPasswordItem(service: KeychainConfiguration.serviceName, account: "Master Password") password = try item.readPassword() } catch { print(error) } textField.text = password if indexPath.row == 0 { cell.textLabel?.text = NSLocalizedString("Password", comment: "") textField.placeholder = "Required" passwordTextField = textField } if indexPath.row == 1 { cell.textLabel?.text = NSLocalizedString("Verify Password", comment: "") textField.placeholder = "Required" verifyPasswordTextField = textField } if indexPath.row == 2 { let cell: UITableViewCell = UITableViewCell() cell.selectionStyle = .none cell.contentView.addSubview(saveButton) saveButton.translatesAutoresizingMaskIntoConstraints = false saveButton.centerXAnchor.constraint(equalTo: cell.centerXAnchor).isActive = true saveButton.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true return cell } textField.isSecureTextEntry = true textField.translatesAutoresizingMaskIntoConstraints = false textField.textAlignment = .right cell.contentView.addSubview(textField) cell.addConstraint(NSLayoutConstraint(item: textField, attribute: .leading, relatedBy: .equal, toItem: cell.textLabel, attribute: .trailing, multiplier: 1, constant: 8)) cell.addConstraint(NSLayoutConstraint(item: textField, attribute: .top, relatedBy: .equal, toItem: cell.contentView, attribute: .top, multiplier: 1, constant: 8)) cell.addConstraint(NSLayoutConstraint(item: textField, attribute: .bottom, relatedBy: .equal, toItem: cell.contentView, attribute: .bottom, multiplier: 1, constant: -8)) cell.addConstraint(NSLayoutConstraint(item: textField, attribute: .trailing, relatedBy: .equal, toItem: cell.contentView, attribute: .trailing, multiplier: 1, constant: -8)) return cell } @objc func saveButtonClicked(sender: UIButton) { guard let passwordTextField = passwordTextField, let verifyPasswordTextField = verifyPasswordTextField, let text = passwordTextField.text else { return } let item = KeychainPasswordItem(service: KeychainConfiguration.serviceName, account: "Master Password") if text.count > 0, text == verifyPasswordTextField.text { do { try item.savePassword(text) } catch { print("Master password saving error: \(error)") } let title = NSLocalizedString("Password has been successfully changed", comment: "") let message = NSLocalizedString("Tip: To use old notes, you must decrypt them separately with the old key and encrypt them again.", comment: "") let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)) present(alert, animated: true, completion: nil) return } let title = NSLocalizedString("Please try again", comment: "") let message = NSLocalizedString("Wrong repeated password", comment: "") let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)) present(alert, animated: true, completion: nil) } } ================================================ FILE: FSNotes iOS/Preferences/SettingsEditorViewController.swift ================================================ // // SettingsEditorViewController.swift // FSNotes iOS // // Created by Олександр Глущенко on 07.08.2020. // Copyright © 2020 Oleksandr Glushchenko. All rights reserved. // import UIKit class SettingsEditorViewController: UITableViewController { private var noteTableUpdater = Timer() private var sections = [ NSLocalizedString("Settings", comment: ""), NSLocalizedString("View", comment: ""), NSLocalizedString("Line Spacing", comment: "Settings"), NSLocalizedString("Font", comment: ""), NSLocalizedString("Code", comment: "") ] private var rowsInSection = [2, 2, 1, 3, 2] private var counter = UILabel(frame: CGRect(x: 0, y: 0, width: 20, height: 20)) private var rows = [ [ NSLocalizedString("Autocorrection", comment: "Settings"), NSLocalizedString("Check Spelling", comment: "Settings"), ], [ NSLocalizedString("Code Block Live Highlighting", comment: "Settings"), NSLocalizedString("MathJax", comment: "Settings"), ], [""], [ NSLocalizedString("Family", comment: "Settings"), NSLocalizedString("Dynamic Type", comment: "Settings"), NSLocalizedString("Font Size", comment: "Settings") ], [ NSLocalizedString("Font", comment: "Settings"), NSLocalizedString("Theme", comment: "Settings"), ] ] override func viewDidLoad() { title = NSLocalizedString("Editor", comment: "Settings") super.viewDidLoad() } @objc func cancel() { self.navigationController?.popViewController(animated: true) } override func numberOfSections(in tableView: UITableView) -> Int { return sections.count } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return rowsInSection[section] } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return sections[section] } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 50 } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if indexPath.section == 3 && indexPath.row == 0 { let controller = FontViewController() self.navigationController?.pushViewController(controller, animated: true) } if indexPath.section == 4 && indexPath.row == 0 { let controller = CodeFontViewController() self.navigationController?.pushViewController(controller, animated: true) } if indexPath.section == 4 && indexPath.row == 1 { let controller = CodeThemeViewController() self.navigationController?.pushViewController(controller, animated: true) } tableView.deselectRow(at: indexPath, animated: false) } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let uiSwitch = UISwitch() uiSwitch.addTarget(self, action: #selector(switchValueDidChange(_:)), for: .valueChanged) let cell = UITableViewCell() cell.textLabel?.text = rows[indexPath.section][indexPath.row] if indexPath.section == 0 { switch indexPath.row { case 0: cell.accessoryView = uiSwitch uiSwitch.isOn = UserDefaultsManagement.editorAutocorrection case 1: cell.accessoryView = uiSwitch uiSwitch.isOn = UserDefaultsManagement.editorSpellChecking default: return cell } } if indexPath.section == 1 { switch indexPath.row { case 0: cell.accessoryView = uiSwitch uiSwitch.isOn = UserDefaultsManagement.codeBlockHighlight case 1: cell.accessoryView = uiSwitch uiSwitch.isOn = UserDefaultsManagement.mathJaxPreview default: return cell } } if indexPath.section == 2 { let brightness = UserDefaultsManagement.editorLineSpacing let slider = UISlider(frame: CGRect(x: 10, y: 3, width: tableView.frame.width - 20, height: 40)) slider.minimumValue = 0 slider.maximumValue = 25 slider.addTarget(self, action: #selector(didChangeLineSpacingSlider), for: .touchUpInside) slider.setValue(brightness, animated: true) cell.addSubview(slider) } if indexPath.section == 3 { switch indexPath.row { case 0: cell.accessoryType = .disclosureIndicator case 1: cell.accessoryView = uiSwitch uiSwitch.isOn = UserDefaultsManagement.dynamicTypeFont case 2: if UserDefaultsManagement.dynamicTypeFont { cell.isHidden = true return cell } let stepper = UIStepper(frame: CGRect(x: 20, y: 20, width: 100, height: 20)) stepper.stepValue = 1 stepper.minimumValue = 10 stepper.maximumValue = 40 stepper.value = Double(UserDefaultsManagement.fontSize) stepper.translatesAutoresizingMaskIntoConstraints = false stepper.addTarget(self, action: #selector(fontSizeChanged), for: .valueChanged) let label = UILabel() label.text = "" label.translatesAutoresizingMaskIntoConstraints = false counter.text = String(Double(UserDefaultsManagement.fontSize)) counter.textColor = UIColor.blackWhite counter.translatesAutoresizingMaskIntoConstraints = false cell.contentView.addSubview(label) cell.contentView.addSubview(counter) cell.contentView.addSubview(stepper) cell.selectionStyle = .none cell.accessoryType = .none let views = ["name" : label, "counter": counter, "stepper" : stepper] as [String : Any] cell.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-10-[name]-[counter(40)]-15-[stepper(100)]-20-|", options: NSLayoutConstraint.FormatOptions.alignAllCenterY, metrics: nil, views: views)) cell.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-10-[name(stepper)]-10-|", options: [], metrics: nil, views: views)) default: return cell } } if indexPath.section == 4 { switch indexPath.row { case 0: cell.accessoryType = .disclosureIndicator break case 1: cell.accessoryType = .disclosureIndicator break default: break } } return cell } override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { if (indexPath.section == 3 && indexPath.row == 2 && UserDefaultsManagement.dynamicTypeFont) { return 0 } return super.tableView(tableView, heightForRowAt: indexPath) } @objc public func switchValueDidChange(_ sender: UISwitch) { guard let cell = sender.superview as? UITableViewCell, let tableView = cell.superview as? UITableView, let indexPath = tableView.indexPath(for: cell) else { return } if indexPath.section == 0 { switch indexPath.row { case 0: guard let uiSwitch = cell.accessoryView as? UISwitch else { return } UserDefaultsManagement.editorAutocorrection = uiSwitch.isOn UIApplication.getEVC().editArea.autocorrectionType = UserDefaultsManagement.editorAutocorrection ? .yes : .no case 1: guard let uiSwitch = cell.accessoryView as? UISwitch else { return } UserDefaultsManagement.editorSpellChecking = uiSwitch.isOn UIApplication.getEVC().editArea.spellCheckingType = UserDefaultsManagement.editorSpellChecking ? .yes : .no default: return } } if indexPath.section == 1 { switch indexPath.row { case 0: guard let uiSwitch = cell.accessoryView as? UISwitch else { return } UserDefaultsManagement.codeBlockHighlight = uiSwitch.isOn case 1: guard let uiSwitch = cell.accessoryView as? UISwitch else { return } UserDefaultsManagement.mathJaxPreview = uiSwitch.isOn default: return } } if indexPath.section == 2 { return } if indexPath.section == 3 { switch indexPath.row { case 0: return case 1: guard let uiSwitch = cell.accessoryView as? UISwitch else { return } UserDefaultsManagement.dynamicTypeFont = uiSwitch.isOn if uiSwitch.isOn { UserDefaultsManagement.fontSize = 17 } if let dynamicCell = tableView.cellForRow(at: IndexPath(row: 2, section: 3)) { dynamicCell.isHidden = uiSwitch.isOn } tableView.reloadRows(at: [IndexPath(row: 2, section: 3)], with: .automatic) noteTableUpdater.invalidate() noteTableUpdater = Timer.scheduledTimer(timeInterval: 1.2, target: self, selector: #selector(self.reloadNotesTable), userInfo: nil, repeats: false) return case 2: return default: return } } } @IBAction func fontSizeChanged(stepper: UIStepper) { UserDefaultsManagement.fontSize = Int(stepper.value) counter.text = String(stepper.value) noteTableUpdater.invalidate() noteTableUpdater = Timer.scheduledTimer(timeInterval: 1.2, target: self, selector: #selector(self.reloadNotesTable), userInfo: nil, repeats: false) } @IBAction func reloadNotesTable() { UIApplication.getVC().notesTable.reloadData() } @objc func didChangeLineSpacingSlider(sender: UISlider) { MPreviewView.template = nil UserDefaultsManagement.editorLineSpacing = sender.value } } ================================================ FILE: FSNotes iOS/Preferences/SettingsTableViewCell.swift ================================================ // // SettingsTableViewCell.swift // FSNotes // // Created by Oleksandr Hlushchenko on 15.09.2024. // Copyright © 2024 Oleksandr Hlushchenko. All rights reserved. // import UIKit class SettingsTableViewCell: UITableViewCell { private let iconView: UIImageView = { let imageView = UIImageView() let symbolConfig = UIImage.SymbolConfiguration(pointSize: 18, weight: .medium) imageView.image = UIImage(systemName: "star.fill", withConfiguration: symbolConfig) imageView.tintColor = .white imageView.translatesAutoresizingMaskIntoConstraints = false return imageView }() private let gradientView: UIView = { let view = UIView() view.translatesAutoresizingMaskIntoConstraints = false return view }() private var iconName: String? private var gradient: [String]? init(iconName: String, gradient: [String], style: UITableViewCell.CellStyle = .subtitle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) self.iconName = iconName self.gradient = gradient let symbolConfig = UIImage.SymbolConfiguration(pointSize: 18, weight: .medium) iconView.image = UIImage(systemName: iconName, withConfiguration: symbolConfig) iconView.tintColor = .white setupViews() applyGradient() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupViews() { contentView.addSubview(gradientView) gradientView.addSubview(iconView) NSLayoutConstraint.activate([ gradientView.widthAnchor.constraint(equalToConstant: 35), gradientView.heightAnchor.constraint(equalToConstant: 35), gradientView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), gradientView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), iconView.centerXAnchor.constraint(equalTo: gradientView.centerXAnchor), iconView.centerYAnchor.constraint(equalTo: gradientView.centerYAnchor), textLabel!.leadingAnchor.constraint(equalTo: gradientView.trailingAnchor, constant: 16), textLabel!.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16), ]) if let detailTextLabel = detailTextLabel { NSLayoutConstraint.activate([ textLabel!.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10), textLabel!.bottomAnchor.constraint(equalTo: detailTextLabel.topAnchor, constant: -4), detailTextLabel.leadingAnchor.constraint(equalTo: textLabel!.leadingAnchor), detailTextLabel.trailingAnchor.constraint(equalTo: textLabel!.trailingAnchor), detailTextLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10) ]) } else { NSLayoutConstraint.activate([ textLabel!.centerYAnchor.constraint(equalTo: contentView.centerYAnchor) ]) } gradientView.layer.cornerRadius = 8 gradientView.clipsToBounds = true textLabel?.translatesAutoresizingMaskIntoConstraints = false detailTextLabel?.translatesAutoresizingMaskIntoConstraints = false } private func applyGradient() { let gradientLayer = CAGradientLayer() let colors = [ UIColor.getBy(hex: self.gradient!.first!).cgColor, UIColor.getBy(hex: self.gradient!.last!).cgColor ] gradientLayer.colors = colors gradientLayer.startPoint = CGPoint(x: 0, y: 0) gradientLayer.endPoint = CGPoint(x: 1, y: 1) gradientLayer.frame = CGRect(x: 0, y: 0, width: 35, height: 35) gradientView.layer.insertSublayer(gradientLayer, at: 0) } } ================================================ FILE: FSNotes iOS/Preferences/SettingsViewController.swift ================================================ // // SettingsViewController.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 2/25/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit import StoreKit import CoreServices import AudioToolbox class SettingsViewController: UITableViewController, UIDocumentPickerDelegate { var sections = [ NSLocalizedString("General", comment: "Settings"), NSLocalizedString("Library", comment: "Settings"), NSLocalizedString("FSNotes", comment: "Settings") ] var rows = [ [ NSLocalizedString("Files", comment: "Settings"), NSLocalizedString("Editor", comment: "Settings"), NSLocalizedString("Security", comment: "Settings"), NSLocalizedString("Git", comment: "Settings"), NSLocalizedString("Icon", comment: "Settings"), NSLocalizedString("Advanced", comment: "Settings"), ], [ NSLocalizedString("iCloud Drive", comment: "Settings"), NSLocalizedString("Add External Folder", comment: "Settings"), NSLocalizedString("Folders", comment: "Settings"), NSLocalizedString("Import Notes", comment: "Settings") ], [ NSLocalizedString("Support", comment: "Settings"), NSLocalizedString("Website", comment: "Settings"), "X", NSLocalizedString("Thanks", comment: "Settings") ] ] var icons = [ [ "doc.badge.gearshape.fill", "paragraphsign", "lock.fill", "arrow.triangle.pull", "square.grid.3x3.middleleft.filled", "atom" ], [ "cloud.fill", "externaldrive.fill.badge.plus", "folder.fill.badge.gearshape", "square.and.arrow.down.fill" ], [ "graduationcap.fill", "house.fill", "x.circle.fill", "heart.fill" ] ] private var gradients = [ [ ["#0a84ff", "#30d158"], ["#ff453a", "#ff9f0a"], ["#bf5af2", "#40c8e0"], ["#8e8e93", "#48484a"], ["#5e5ce6", "#8e8e93"], ["#dc1c13", "#f07470"] ], [ ["#009bf9", "#004D7C"], ["#614385", "#516395"], ["#EA8D8D", "#A890FE"], ["#0D7A25", "#40AD58"] ], [ ["#dfbd69", "#926f34"], ["#09203F", "#537895"], ["#868F96", "#596164"], ["#ff9966", "#ff5e62"] ] ] var rowsInSection = [6, 4, 4] override func viewWillAppear(_ animated: Bool) { navigationController?.navigationBar.prefersLargeTitles = true } override func viewDidLoad() { title = NSLocalizedString("Settings", comment: "Sidebar settings") navigationItem.rightBarButtonItem = Buttons.getRateUs(target: self, selector: #selector(rateUs)) super.viewDidLoad() let version = UILabel(frame: CGRect(x: 8, y: 30, width: tableView.frame.width, height: 60)) version.font = version.font.withSize(17).bold() if let versionString = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String, let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String { version.text = NSLocalizedString("Version", comment: "Settings") + " \(versionString) " + NSLocalizedString("build", comment: "Settings") + " \(build)" } version.textColor = UIColor.lightGray version.textAlignment = .center tableView.tableFooterView = version navigationController?.navigationBar.prefersLargeTitles = true } override func numberOfSections(in tableView: UITableView) -> Int { return 3 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return rowsInSection[section] } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return sections[section] } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 50 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let iconName = icons[indexPath.section][indexPath.row] let gradient = gradients[indexPath.section][indexPath.row] var cell = SettingsTableViewCell(iconName: iconName, gradient: gradient, style: .default, reuseIdentifier: iconName) if indexPath.section == 0x01 && indexPath.row == 0x03 { cell = SettingsTableViewCell(iconName: iconName, gradient: gradient, style: .subtitle, reuseIdentifier: iconName) } cell.textLabel?.text = rows[indexPath.section][indexPath.row] if indexPath.section == 0x00 { cell.accessoryType = .disclosureIndicator return cell } if indexPath.section == 0x01 { switch indexPath.row { case 0: let uiSwitch = UISwitch() uiSwitch.addTarget(self, action: #selector(switchValueDidChange(_:)), for: .valueChanged) uiSwitch.isOn = UserDefaultsManagement.iCloudDrive cell.textLabel?.text = "iCloud Drive" cell.accessoryView = uiSwitch case 1: cell.accessoryType = .none case 2: cell.accessoryType = .disclosureIndicator case 3: cell.detailTextLabel?.textColor = UIColor.blackWhite cell.detailTextLabel?.numberOfLines = 0 cell.detailTextLabel?.lineBreakMode = .byWordWrapping cell.detailTextLabel?.text = NSLocalizedString("Compatible with Bear and Ulysses (textbundle), markdown, txt.", comment: "") default: return cell } } if indexPath.section == 0x02 && indexPath.row == 0x03 { cell.accessoryType = .disclosureIndicator return cell } return cell } private func image( _ image:UIImage, withSize newSize:CGSize) -> UIImage { UIGraphicsBeginImageContextWithOptions(newSize, false, UIScreen.main.scale) image.draw(in: CGRect(x: 0,y: 0,width: newSize.width,height: newSize.height)) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage!.withRenderingMode(.automatic) } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { defer { tableView.deselectRow(at: indexPath, animated: false) } var lvc: UIViewController? if indexPath.section == 0x00 { switch indexPath.row { case 0: lvc = DefaultExtensionViewController() case 1: lvc = SettingsEditorViewController() case 2: lvc = SecurityViewController() case 3: guard let project = Storage.shared().getDefault() else { return } lvc = AppDelegate.getGitVC(for: project) case 4: lvc = AppIconViewController() case 5: lvc = ProViewController() default: return } } if indexPath.section == 0x01 { switch indexPath.row { case 0: break case 1: if #available(iOS 13.0, *) { let viewController = ExternalViewController(forOpeningContentTypes: [.folder], asCopy: false) viewController.delegate = viewController present(viewController, animated: true, completion: nil) } break case 2: lvc = ProjectsViewController() break case 3: var picker: UIDocumentPickerViewController if #available(iOS 14.0, *) { picker = UIDocumentPickerViewController(forOpeningContentTypes: [.item]) } else { picker = UIDocumentPickerViewController(documentTypes: ["public.item"], in: .import) } picker.allowsMultipleSelection = true picker.delegate = self self.present(picker, animated: true, completion: nil) break default: break } } if indexPath.section == 0x02 { var url: URL? switch indexPath.row { case 0x00: url = URL(string: "https://github.com/glushchenko/fsnotes/issues") break case 0x01: url = URL(string: "https://fsnot.es") break case 0x02: url = URL(string: "https://twitter.com/fsnotesapp") break case 0x03: lvc = ThanksViewController() break default: break } if let url = url { UIApplication.shared.open(url, options: [:], completionHandler: nil) } } if let controller = lvc { self.navigationController?.pushViewController(controller, animated: true) } } func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { guard let storageUrl = UserDefaultsManagement.storageUrl else { return } for url in urls { try? FileManager.default.copyItem(at: url, to: storageUrl.appendingPathComponent(url.lastPathComponent)) } } @objc func rateUs() { if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene { DispatchQueue.main.async { AudioServicesPlaySystemSound(1519) SKStoreReviewController.requestReview(in: scene) } } } @objc func done() { navigationController?.popViewController(animated: true) } @objc public func switchValueDidChange(_ sender: UISwitch) { guard let cell = sender.superview as? UITableViewCell else { return } guard let uiSwitch = cell.accessoryView as? UISwitch else { return } UserDefaultsManagement.iCloudDrive = uiSwitch.isOn UIApplication.getVC().reloadDatabase() if !uiSwitch.isOn { UIApplication.getVC().stopCloudDriveSyncEngine() } } } ================================================ FILE: FSNotes iOS/Preferences/SidebarViewController.swift ================================================ // // SidebarViewController.swift // FSNotes iOS // // Created by Александр on 08.03.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // import UIKit class SidebarViewController: UITableViewController { private var rows = [ NSLocalizedString("Notes", comment: ""), NSLocalizedString("Inbox", comment: ""), NSLocalizedString("Todo", comment: ""), NSLocalizedString("Untagged", comment: ""), NSLocalizedString("Trash", comment: ""), ] override func viewDidLoad() { self.title = NSLocalizedString("Library", comment: "Settings") super.viewDidLoad() } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: false) } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return rows.count } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 50 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell() cell.textLabel?.text = rows[indexPath.row] let uiSwitch = UISwitch() uiSwitch.addTarget(self, action: #selector(switchValueDidChange(_:)), for: .valueChanged) switch indexPath.row { case 0: uiSwitch.isOn = UserDefaultsManagement.sidebarVisibilityNotes break case 1: uiSwitch.isOn = UserDefaultsManagement.sidebarVisibilityInbox break case 2: uiSwitch.isOn = UserDefaultsManagement.sidebarVisibilityTodo break case 3: uiSwitch.isOn = UserDefaultsManagement.sidebarVisibilityUntagged break case 4: uiSwitch.isOn = UserDefaultsManagement.sidebarVisibilityTrash break default: break } cell.accessoryView = uiSwitch return cell } @objc func cancel() { navigationController?.popViewController(animated: true) } @objc public func switchValueDidChange(_ sender: UISwitch) { guard let cell = sender.superview as? UITableViewCell, let tableView = cell.superview as? UITableView, let indexPath = tableView.indexPath(for: cell) else { return } guard let uiSwitch = cell.accessoryView as? UISwitch else { return } switch indexPath.row { case 0: UserDefaultsManagement.sidebarVisibilityNotes = uiSwitch.isOn case 1: UserDefaultsManagement.sidebarVisibilityInbox = uiSwitch.isOn case 2: UserDefaultsManagement.sidebarVisibilityTodo = uiSwitch.isOn case 3: UserDefaultsManagement.sidebarVisibilityUntagged = uiSwitch.isOn case 4: UserDefaultsManagement.sidebarVisibilityTrash = uiSwitch.isOn default: return } UIApplication.getVC().sidebarTableView.reloadSidebar() } } ================================================ FILE: FSNotes iOS/Preferences/SortByViewController.swift ================================================ // // SortByViewController.swift // FSNotes iOS // // Created by Александр on 06.03.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // import UIKit class SortByViewController: UITableViewController { private var rows = [ NSLocalizedString("Modification Date", comment: ""), NSLocalizedString("Creation Date", comment: ""), NSLocalizedString("Title", comment: "") ] override func viewDidLoad() { self.title = NSLocalizedString("Sort By", comment: "Settings") super.viewDidLoad() } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let vc = UIApplication.getVC() if let cell = tableView.cellForRow(at: indexPath) { if indexPath.section == 0x00 { for row in 0...rows.count { let cell = tableView.cellForRow(at: IndexPath(row: row, section: 0)) cell?.accessoryType = .none } if let sort = SortBy(rawValue: cell.reuseIdentifier!) { UserDefaultsManagement.sort = sort vc.reloadNotesTable() } if cell.accessoryType == .none { cell.accessoryType = .checkmark } else { cell.accessoryType = .none } } } cancel() } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return rows.count } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 50 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { var cell = UITableViewCell() if indexPath.section == 0x00 { switch indexPath.row { case 0: cell = UITableViewCell(style: .default, reuseIdentifier: "modificationDate") cell.textLabel?.text = NSLocalizedString("Modification Date", comment: "") if UserDefaultsManagement.sort == .modificationDate { cell.accessoryType = .checkmark } break case 1: cell = UITableViewCell(style: .default, reuseIdentifier: "creationDate") cell.textLabel?.text = NSLocalizedString("Creation Date", comment: "") if UserDefaultsManagement.sort == .creationDate { cell.accessoryType = .checkmark } break case 2: cell = UITableViewCell(style: .default, reuseIdentifier: "title") cell.textLabel?.text = NSLocalizedString("Title", comment: "") if UserDefaultsManagement.sort == .title { cell.accessoryType = .checkmark } break default: break } } return cell } @objc func cancel() { navigationController?.popViewController(animated: true) } @objc func close() { dismiss(animated: true, completion: nil) } } ================================================ FILE: FSNotes iOS/Preferences/ThanksViewController.swift ================================================ // // ThanksViewController.swift // FSNotes iOS // // Created by Александр on 06.03.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // import UIKit class ThanksViewController: UITableViewController { private var rows = [ "Radio-T", "Matt Septhon", "Dylan Seeger (Icon design)" ] private var urls = [ "https://radio-t.com", "https://www.gingerbeardman.com", "https://lovably.com" ] override func viewDidLoad() { self.title = NSLocalizedString("Thanks", comment: "Settings") super.viewDidLoad() } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let url = URL(string: urls[indexPath.row]) { UIApplication.shared.open(url, options: [:], completionHandler: nil) } tableView.deselectRow(at: indexPath, animated: false) } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return rows.count } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 50 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell() cell.textLabel?.text = rows[indexPath.row] return cell } @objc func cancel() { navigationController?.popViewController(animated: true) } } ================================================ FILE: FSNotes iOS/RevisionsViewController.swift ================================================ // // RevisionsViewController.swift // FSNotes iOS // // Created by Александр on 14.02.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // import Foundation import UIKit class RevisionsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { @IBOutlet weak var navItem: UINavigationItem! @IBOutlet weak var navigationBar: UINavigationBar! @IBOutlet weak var bottomSafeView: UIView! @IBOutlet weak var revisionsTable: UITableView! public var note: Note? private var revisions = [Revision]() override func viewDidLoad() { super.viewDidLoad() navigationBar.barTintColor = UIColor.sidebar navigationBar.tintColor = UIColor.mainTheme navigationBar.backgroundColor = UIColor.sidebar bottomSafeView.backgroundColor = UIColor.sidebar if let urls = note?.listRevisions() { revisions = urls } revisionsTable.delegate = self revisionsTable.dataSource = self initButtons() } private func initButtons() { var buttons = [UIBarButtonItem]() let leftString = NSLocalizedString("Cancel", comment: "") navItem.leftBarButtonItem = UIBarButtonItem(title: leftString, style: .plain, target: self, action: #selector(closeController)) if let project = note?.project, !project.hasRepository() { let dropImage = UIImage(systemName: "trash") let dropBarButton = UIBarButtonItem(image: dropImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(dropRevisions)) buttons.append(dropBarButton) } let saveImage = UIImage(systemName: "plus.circle") let saveBarButton = UIBarButtonItem(image: saveImage, landscapeImagePhone: nil, style: .done, target: self, action: #selector(saveRevision)) buttons.append(saveBarButton) navItem.rightBarButtonItems = buttons } @IBAction func dropRevisions() { let title = NSLocalizedString("Сlearing history", comment: "") let message = NSLocalizedString("Are you sure you want to delete all versions of this note?", comment: "") let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default) { (_) in self.note?.dropRevisions() self.dismiss(animated: true) }) let cancel = NSLocalizedString("Cancel", comment: "") alert.addAction(UIAlertAction(title: cancel, style: .cancel, handler: { (action: UIAlertAction!) in })) self.present(alert, animated: true, completion: nil) } @IBAction func saveRevision() { guard let note = note else { return } UIApplication.getVC().notesTable.saveRevisionAction(note: note) dismiss(animated: true) } @IBAction func closeController() { dismiss(animated: true) } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return revisions.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell() let date = Date(timeIntervalSince1970: revisions[indexPath.row].timestamp) let dateFormatter = DateFormatter() dateFormatter.timeStyle = DateFormatter.Style.medium //Set time style dateFormatter.dateStyle = DateFormatter.Style.medium //Set date style dateFormatter.timeZone = .current let localDate = dateFormatter.string(from: date) cell.textLabel?.text = localDate return cell } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 70 } func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return NSLocalizedString("Saved versions", comment: "") } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 100 } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let revision = revisions[indexPath.row] note?.restore(revision: revision) UIApplication.getEVC().refill() dismiss(animated: true) } func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) { guard let headerView = view as? UITableViewHeaderFooterView else { return } headerView.textLabel?.font = .preferredFont(forTextStyle: .title1, compatibleWith: nil) } } ================================================ FILE: FSNotes iOS/SceneDelegate.swift ================================================ // // SceneDelegate.swift // FSNotes // // Created by Oleksandr Hlushchenko on 15.11.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? var launchedShortcutItem: UIApplicationShortcutItem? var listController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "listViewController") as! ViewController var editorController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "editorViewController") as! EditorViewController var mainController: MainNavigationController? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } // Handle shortcut from cold launch if let shortcutItem = connectionOptions.shortcutItem { launchedShortcutItem = shortcutItem } window = UIWindow(windowScene: windowScene) let nav = MainNavigationController(rootViewController: listController) nav.setNavigationBarHidden(false, animated: false) mainController = nav window?.rootViewController = nav window?.makeKeyAndVisible() editorController.loadViewIfNeeded() DispatchQueue.main.async { [weak self] in guard let self = self else { return } if let shortcutItem = self.launchedShortcutItem { self.handle(shortcutItem: shortcutItem) } if let urlContext = connectionOptions.urlContexts.first { self.handle(url: urlContext.url) } if let userActivity = connectionOptions.userActivities.first ?? session.stateRestorationActivity { self.configure(window: self.window, with: userActivity) } } } func sceneDidDisconnect(_ scene: UIScene) { // Called as the scene is being released by the system. } func sceneDidBecomeActive(_ scene: UIScene) { // Called when the scene has moved from an inactive state to an active state. } func sceneWillResignActive(_ scene: UIScene) { // Called when the scene will move from an active state to an inactive state. } func sceneWillEnterForeground(_ scene: UIScene) { // Called as the scene transitions from the background to the foreground. } func sceneDidEnterBackground(_ scene: UIScene) { // Called as the scene transitions from the foreground to the background. saveEditorState() } // MARK: - Shortcut Actions func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { handle(shortcutItem: shortcutItem) completionHandler(true) } private func handle(shortcutItem: UIApplicationShortcutItem) { if ShortcutIdentifier(fullType: shortcutItem.type) == .search { UIApplication.getVC().enableSearchFocus() } UIApplication.getVC().handleShortCutItem(shortcutItem) } // MARK: - URL Handling func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { guard let url = URLContexts.first?.url else { return } handle(url: url) } private func handle(url: URL) { let vc = UIApplication.getVC() let storage = Storage.shared() var note = storage.getBy(url: url) if url.host == "open" { if let tag = url["tag"]?.removingPercentEncoding { vc.sidebarTableView.select(tag: tag) mainController?.popToRootViewController(animated: true) return } } if url.host == "find" { if let id = url["id"]?.removingPercentEncoding { note = storage.getBy(title: id) if !vc.isLoadedDB, note == nil { vc.restoreFindID = id return } } } if let note = note { UIApplication.getEVC().fill(note: note) UIApplication.getVC().openEditorViewController() print("File imported: \(note.url)") } else { guard url.startAccessingSecurityScopedResource(), let inbox = storage.getDefault() else { return } let dst = NameHelper.getUniqueFileName(name: "", project: inbox, ext: url.pathExtension) do { try FileManager.default.copyItem(at: url, to: dst) if let note = storage.importNote(url: dst) { vc.notesTable.insertRows(notes: [note]) vc.updateNotesCounter() } } catch { print("Note opening error: \(error)") } } } // MARK: - State Restoration func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { configure(window: window, with: userActivity) } func configure(window: UIWindow?, with activity: NSUserActivity) { UIApplication.getEVC().restoreUserActivityState(activity) } func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? { return scene.userActivity } // MARK: - Helper Methods private func saveEditorState() { let evc = UIApplication.getEVC() guard evc.navigationController?.topViewController === evc else { return } if let url = evc.note?.url { UserDefaultsManagement.currentEditorState = evc.editArea.isFirstResponder if evc.note?.previewState == true { UserDefaultsManagement.currentRange = nil } else { UserDefaultsManagement.currentRange = evc.editArea.selectedRange } UserDefaultsManagement.currentNote = url } } } ================================================ FILE: FSNotes iOS/View/EditTextView.swift ================================================ // // EditTextView.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 1/29/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit import MobileCoreServices import UniformTypeIdentifiers class EditTextView: UITextView, UITextViewDelegate { public var textStorageProcessor: TextStorageProcessor? public var isNoteLoading = false public var isAllowedScrollRect: Bool? public var typingFont: UIFont? public var note: Note? public var lasTouchPoint: CGPoint? public var imagesLoaderQueue = OperationQueue.init() public var keyboardIsOpened = true public var callCounter = 0 public var isUpdating = false required init?(coder: NSCoder) { if #available(iOS 13.2, *) { super.init(coder: coder) } else { super.init(frame: .zero, textContainer: nil) self.autoresizingMask = [.flexibleWidth, .flexibleHeight] self.contentMode = .scaleToFill self.isScrollEnabled = false // causes expanding height // Auto Layout self.translatesAutoresizingMaskIntoConstraints = false self.font = UIFont(name: "HelveticaNeue", size: 18) } autocorrectionType = UserDefaultsManagement.editorAutocorrection ? .yes : .no spellCheckingType = UserDefaultsManagement.editorSpellChecking ? .yes : .no } override func becomeFirstResponder() -> Bool { textStorage.removeHighlight() return super.becomeFirstResponder() } override func touchesEnded(_ touches: Set, with event: UIEvent?) { super.touchesEnded(touches, with: event) if !isFirstResponder && window != nil { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in guard let self = self else { return } if !self.isFirstResponder && self.window != nil { _ = self.becomeFirstResponder() } } } } public func initTextStorage() { let processor = TextStorageProcessor() processor.editor = self textStorageProcessor = processor textStorage.delegate = processor } override func selectionRects(for range: UITextRange) -> [UITextSelectionRect] { let selectionRects = super.selectionRects(for: range) let fontHeight = UserDefaultsManagement.noteFont.lineHeight let lineSpacing = CGFloat(UserDefaultsManagement.editorLineSpacing) let endCharacterIndex = offset(from: beginningOfDocument, to: range.end) let endParRange = textStorage.mutableString.paragraphRange(for: NSRange(location: endCharacterIndex, length: 0)) var lastWideRect: UITextSelectionRect? if selectionRects.count > 2 { lastWideRect = selectionRects[selectionRects.count - 3] } var result = [UITextSelectionRect]() for selectionRect in selectionRects { if selectionRect.rect.width == 0 { let customRect = CGRect(x: selectionRect.rect.origin.x, y: selectionRect.rect.origin.y - lineSpacing / 2, width: 0, height: fontHeight + lineSpacing) let sel = EditorSelectionRect(originalRect: selectionRect, rect: customRect) result.append(sel) } else { var heightOffset = CGFloat(0) if endParRange.upperBound == textStorage.length && lastWideRect == selectionRect { heightOffset += lineSpacing } let customRect = CGRect(x: selectionRect.rect.origin.x, y: selectionRect.rect.origin.y - lineSpacing / 2, width: selectionRect.rect.width, height: selectionRect.rect.height + heightOffset) let selectionRect = EditorSelectionRect(originalRect: selectionRect, rect: customRect) result.append(selectionRect) } } return result } override func caretRect(for position: UITextPosition) -> CGRect { let characterIndex = offset(from: beginningOfDocument, to: position) guard layoutManager.isValidGlyphIndex(characterIndex) else { return super.caretRect(for: position) } let glyphIndex = layoutManager.glyphIndexForCharacter(at: characterIndex) let usedLineFragment = layoutManager.lineFragmentUsedRect(forGlyphAt: glyphIndex, effectiveRange: nil) guard !usedLineFragment.isEmpty else { return super.caretRect(for: position) } var caretRect = super.caretRect(for: position) caretRect.origin.y = usedLineFragment.origin.y + textContainerInset.top caretRect.size.height = usedLineFragment.size.height - CGFloat(UserDefaultsManagement.editorLineSpacing) / 2 return caretRect } override func scrollRectToVisible(_ rect: CGRect, animated: Bool) { guard isAllowedScrollRect == true else { return } callCounter += 1 if keyboardIsOpened { DispatchQueue.main.async { UIView.animate(withDuration: 0.8, delay: 0, options: .beginFromCurrentState, animations: { super.scrollRectToVisible(rect, animated: false) }) } if callCounter > 2 { keyboardIsOpened = false callCounter = 0 } } else { super.scrollRectToVisible(rect, animated: animated) } } override func cut(_ sender: Any?) { let range = selectedRange guard range.length > 0 else { return } let selectedString = textStorage.attributedSubstring(from: range) let attributedString = NSMutableAttributedString(attributedString: selectedString) .unloadTasks() attributedString.saveData() super.cut(sender) do { let data = try NSKeyedArchiver.archivedData( withRootObject: attributedString, requiringSecureCoding: false ) UIPasteboard.general.setItems([ [ UIPasteboard.attributed: data, UTType.plainText.identifier: attributedString.string ] ]) } catch { print("Serialization error: \(error)") } } override func paste(_ sender: Any?) { isUpdating = true defer { DispatchQueue.main.async { self.isUpdating = false } } let pb = UIPasteboard.general var toInsert: NSAttributedString? if let imageData = pb.data(forPasteboardType: UTType.png.identifier) ?? pb.data(forPasteboardType: UTType.jpeg.identifier) ?? pb.data(forPasteboardType: UTType.image.identifier) { toInsert = NSMutableAttributedString.build(data: imageData) } else if let data = pb.data(forPasteboardType: UIPasteboard.attributed) { do { if let attributed = try NSKeyedUnarchiver.unarchivedObject( ofClass: NSAttributedString.self, from: data ) { let mutable = NSMutableAttributedString(attributedString: attributed) mutable.loadTasks() toInsert = mutable } } catch { print("Paste error: \(error)") } } else if let plain = pb.string { let mutable = NSMutableAttributedString(string: plain) mutable.loadTasks() mutable.loadFont() toInsert = mutable } guard let attrToInsert = toInsert else { super.paste(sender) return } let range = self.selectedRange if let should = delegate?.textView?(self, shouldChangeTextIn: range, replacementText: attrToInsert.string), !should { return } self.insertAttributedText(attrToInsert) } override func copy(_ sender: Any?) { guard selectedRange.length > 0 else { return } let selectedString = textStorage.attributedSubstring(from: self.selectedRange) let attributedString = NSMutableAttributedString(attributedString: selectedString).unloadTasks() attributedString.saveData() do { let data = try NSKeyedArchiver.archivedData( withRootObject: attributedString, requiringSecureCoding: false ) UIPasteboard.general.setItems([ [ UIPasteboard.attributed: data, UTType.plainText.identifier: attributedString.string ] ]) return } catch { print("Serialization error: \(error)") } super.copy(sender) } override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { if action == #selector(UIResponderStandardEditActions.paste(_:)) { return true } return super.canPerformAction(action, withSender: sender) } public func initUndoRedoButons() { UIApplication.getEVC().undoBarButton?.isEnabled = undoManager?.canUndo == true UIApplication.getEVC().redoBarButton?.isEnabled = undoManager?.canRedo == true } public func isTodo(at location: Int) -> Bool { let storage = self.textStorage if storage.length > location, storage.attribute(.todo, at: location, effectiveRange: nil) != nil { return true } let range = (storage.string as NSString).paragraphRange(for: NSRange(location: location, length: 0)) let string = storage.attributedSubstring(from: range).string as NSString var length = string.range(of: "- [ ]").length if length == 0 { length = string.range(of: "- [x]").length } if length > 0 { let upper = range.location + length if location >= range.location && location <= upper { return true } } return false } public func isImage(at location: Int) -> Bool { return textStorage.getMeta(at: location) != nil } public func isLink(at location: Int) -> Bool { let storage = self.textStorage if storage.length > location, storage.attribute(.link, at: location, effectiveRange: nil) != nil { return true } return false } public func isWikiLink(at location: Int) -> Bool { let storage = self.textStorage if storage.length > location, let path = storage.attribute(.link, at: location, effectiveRange: nil) as? String, path.starts(with: "fsnotes://find?id=") { return true } return false } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) UIApplication.getEVC().themeObserver() } } struct Undo { var range: NSRange var string: NSAttributedString } ================================================ FILE: FSNotes iOS/View/EditorSelectionRect.swift ================================================ // // EditorSelectionRect.swift // FSNotes iOS // // Created by Александр on 11.02.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // import UIKit class EditorSelectionRect: UITextSelectionRect { private let original: UITextSelectionRect private var customRect: CGRect? = nil override var rect: CGRect { if let customRect = customRect { return customRect } return original.rect } override var writingDirection: NSWritingDirection { return original.writingDirection } override var containsStart: Bool { return original.containsStart } override var containsEnd: Bool { return original.containsEnd } override var isVertical: Bool { return original.isVertical } init(originalRect original: UITextSelectionRect, rect: CGRect) { self.original = original self.customRect = rect } } ================================================ FILE: FSNotes iOS/View/ImageScrollView.swift ================================================ // // ImageScrollView.swift // Beauty // // Created by Nguyen Cong Huy on 1/19/16. // Copyright © 2016 Nguyen Cong Huy. All rights reserved. // import UIKit @objc public protocol ImageScrollViewDelegate: UIScrollViewDelegate { func imageScrollViewDidChangeOrientation(imageScrollView: ImageScrollView) } open class ImageScrollView: UIScrollView { @objc public enum ScaleMode: Int { case aspectFill case aspectFit case widthFill case heightFill } @objc public enum Offset: Int { case begining case center } static let kZoomInFactorFromMinWhenDoubleTap: CGFloat = 2 @objc open var imageContentMode: ScaleMode = .widthFill @objc open var initialOffset: Offset = .begining @objc public private(set) var zoomView: UIImageView? = nil @objc open weak var imageScrollViewDelegate: ImageScrollViewDelegate? var imageSize: CGSize = CGSize.zero private var pointToCenterAfterResize: CGPoint = CGPoint.zero private var scaleToRestoreAfterResize: CGFloat = 1.0 open var maxScaleFromMinScale: CGFloat = 3.0 override open var frame: CGRect { willSet { if frame.equalTo(newValue) == false && newValue.equalTo(CGRect.zero) == false && imageSize.equalTo(CGSize.zero) == false { prepareToResize() } } didSet { if frame.equalTo(oldValue) == false && frame.equalTo(CGRect.zero) == false && imageSize.equalTo(CGSize.zero) == false { recoverFromResizing() } } } override public init(frame: CGRect) { super.init(frame: frame) initialize() } required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) initialize() } deinit { NotificationCenter.default.removeObserver(self) } private func initialize() { showsVerticalScrollIndicator = false showsHorizontalScrollIndicator = false bouncesZoom = true decelerationRate = UIScrollView.DecelerationRate.fast delegate = self NotificationCenter.default.addObserver(self, selector: #selector(ImageScrollView.changeOrientationNotification), name: UIDevice.orientationDidChangeNotification, object: nil) } @objc public func adjustFrameToCenter() { guard let unwrappedZoomView = zoomView else { return } var frameToCenter = unwrappedZoomView.frame // center horizontally if frameToCenter.size.width < bounds.width { frameToCenter.origin.x = (bounds.width - frameToCenter.size.width) / 2 } else { frameToCenter.origin.x = 0 } // center vertically if frameToCenter.size.height < bounds.height { frameToCenter.origin.y = (bounds.height - frameToCenter.size.height) / 2 } else { frameToCenter.origin.y = 0 } unwrappedZoomView.frame = frameToCenter } private func prepareToResize() { let boundsCenter = CGPoint(x: bounds.midX, y: bounds.midY) pointToCenterAfterResize = convert(boundsCenter, to: zoomView) scaleToRestoreAfterResize = zoomScale // If we're at the minimum zoom scale, preserve that by returning 0, which will be converted to the minimum // allowable scale when the scale is restored. if scaleToRestoreAfterResize <= minimumZoomScale + CGFloat(Float.ulpOfOne) { scaleToRestoreAfterResize = 0 } } private func recoverFromResizing() { setMaxMinZoomScalesForCurrentBounds() // restore zoom scale, first making sure it is within the allowable range. let maxZoomScale = max(minimumZoomScale, scaleToRestoreAfterResize) zoomScale = min(maximumZoomScale, maxZoomScale) // restore center point, first making sure it is within the allowable range. // convert our desired center point back to our own coordinate space let boundsCenter = convert(pointToCenterAfterResize, to: zoomView) // calculate the content offset that would yield that center point var offset = CGPoint(x: boundsCenter.x - bounds.size.width/2.0, y: boundsCenter.y - bounds.size.height/2.0) // restore offset, adjusted to be within the allowable range let maxOffset = maximumContentOffset() let minOffset = minimumContentOffset() var realMaxOffset = min(maxOffset.x, offset.x) offset.x = max(minOffset.x, realMaxOffset) realMaxOffset = min(maxOffset.y, offset.y) offset.y = max(minOffset.y, realMaxOffset) contentOffset = offset } private func maximumContentOffset() -> CGPoint { return CGPoint(x: contentSize.width - bounds.width,y:contentSize.height - bounds.height) } private func minimumContentOffset() -> CGPoint { return CGPoint.zero } // MARK: - Set up open func setup() { var topSupperView = superview while topSupperView?.superview != nil { topSupperView = topSupperView?.superview } // Make sure views have already layout with precise frame topSupperView?.layoutIfNeeded() DispatchQueue.main.async { self.refresh() } } // MARK: - Display image @objc open func display(image: UIImage) { if let zoomView = zoomView { zoomView.removeFromSuperview() } zoomView = UIImageView(image: image) zoomView!.isUserInteractionEnabled = true addSubview(zoomView!) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ImageScrollView.doubleTapGestureRecognizer(_:))) tapGesture.numberOfTapsRequired = 2 zoomView!.addGestureRecognizer(tapGesture) configureImageForSize(image.size) } private func configureImageForSize(_ size: CGSize) { imageSize = size contentSize = imageSize setMaxMinZoomScalesForCurrentBounds() zoomScale = minimumZoomScale switch initialOffset { case .begining: contentOffset = CGPoint.zero case .center: let xOffset = contentSize.width < bounds.width ? 0 : (contentSize.width - bounds.width)/2 let yOffset = contentSize.height < bounds.height ? 0 : (contentSize.height - bounds.height)/2 switch imageContentMode { case .aspectFit: contentOffset = CGPoint.zero case .aspectFill: contentOffset = CGPoint(x: xOffset, y: yOffset) case .heightFill: contentOffset = CGPoint(x: xOffset, y: 0) case .widthFill: contentOffset = CGPoint(x: 0, y: yOffset) } } } private func setMaxMinZoomScalesForCurrentBounds() { // calculate min/max zoomscale let xScale = bounds.width / imageSize.width // the scale needed to perfectly fit the image width-wise let yScale = bounds.height / imageSize.height // the scale needed to perfectly fit the image height-wise var minScale: CGFloat = 1 switch imageContentMode { case .aspectFill: minScale = max(xScale, yScale) case .aspectFit: minScale = min(xScale, yScale) case .widthFill: minScale = xScale case .heightFill: minScale = yScale } let maxScale = maxScaleFromMinScale*minScale // don't let minScale exceed maxScale. (If the image is smaller than the screen, we don't want to force it to be zoomed.) if minScale > maxScale { minScale = maxScale } maximumZoomScale = maxScale minimumZoomScale = minScale * 0.999 // the multiply factor to prevent user cannot scroll page while they use this control in UIPageViewController } // MARK: - Gesture @objc func doubleTapGestureRecognizer(_ gestureRecognizer: UIGestureRecognizer) { // zoom out if it bigger than the scale factor after double-tap scaling. Else, zoom in if zoomScale >= minimumZoomScale * ImageScrollView.kZoomInFactorFromMinWhenDoubleTap - 0.01 { setZoomScale(minimumZoomScale, animated: true) } else { let center = gestureRecognizer.location(in: gestureRecognizer.view) let zoomRect = zoomRectForScale(ImageScrollView.kZoomInFactorFromMinWhenDoubleTap * minimumZoomScale, center: center) zoom(to: zoomRect, animated: true) } } private func zoomRectForScale(_ scale: CGFloat, center: CGPoint) -> CGRect { var zoomRect = CGRect.zero // the zoom rect is in the content view's coordinates. // at a zoom scale of 1.0, it would be the size of the imageScrollView's bounds. // as the zoom scale decreases, so more content is visible, the size of the rect grows. zoomRect.size.height = frame.size.height / scale zoomRect.size.width = frame.size.width / scale // choose an origin so as to get the right center. zoomRect.origin.x = center.x - (zoomRect.size.width / 2.0) zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0) return zoomRect } open func refresh() { if let image = zoomView?.image { display(image: image) } } // MARK: - Actions @objc func changeOrientationNotification() { // A weird bug that frames are not update right after orientation changed. Need delay a little bit with async. DispatchQueue.main.async { self.configureImageForSize(self.imageSize) self.imageScrollViewDelegate?.imageScrollViewDidChangeOrientation(imageScrollView: self) } } } extension ImageScrollView: UIScrollViewDelegate { public func scrollViewDidScroll(_ scrollView: UIScrollView) { imageScrollViewDelegate?.scrollViewDidScroll?(scrollView) } public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { imageScrollViewDelegate?.scrollViewWillBeginDragging?(scrollView) } public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { imageScrollViewDelegate?.scrollViewWillEndDragging?(scrollView, withVelocity: velocity, targetContentOffset: targetContentOffset) } public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { imageScrollViewDelegate?.scrollViewDidEndDragging?(scrollView, willDecelerate: decelerate) } public func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) { imageScrollViewDelegate?.scrollViewWillBeginDecelerating?(scrollView) } public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { imageScrollViewDelegate?.scrollViewDidEndDecelerating?(scrollView) } public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { imageScrollViewDelegate?.scrollViewDidEndScrollingAnimation?(scrollView) } public func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) { imageScrollViewDelegate?.scrollViewWillBeginZooming?(scrollView, with: view) } public func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) { imageScrollViewDelegate?.scrollViewDidEndZooming?(scrollView, with: view, atScale: scale) } public func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool { return false } @available(iOS 11.0, *) public func scrollViewDidChangeAdjustedContentInset(_ scrollView: UIScrollView) { imageScrollViewDelegate?.scrollViewDidChangeAdjustedContentInset?(scrollView) } public func viewForZooming(in scrollView: UIScrollView) -> UIView? { return zoomView } public func scrollViewDidZoom(_ scrollView: UIScrollView) { adjustFrameToCenter() imageScrollViewDelegate?.scrollViewDidZoom?(scrollView) } } ================================================ FILE: FSNotes iOS/View/NoteCellView.swift ================================================ // // NoteCellView.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 1/29/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit import SwipeCellKit class NoteCellView: SwipeTableViewCell { @IBOutlet weak var title: UILabel! @IBOutlet weak var date: UILabel! @IBOutlet weak var preview: UILabel! @IBOutlet weak var pin: UIImageView! @IBOutlet weak var imagePreview: UIImageView! @IBOutlet weak var imagePreviewSecond: UIImageView! @IBOutlet weak var imagePreviewThird: UIImageView! public var note: Note? public var contentLength: Int = 0 public var timestamp: Int64? public var imageKeys = [String]() public var tableView: NotesTableView? { get { return self.superview as? NotesTableView } } override func prepareForReuse() { super.prepareForReuse() imagePreview.image = nil imagePreviewSecond.image = nil imagePreviewThird.image = nil imagePreview.isHidden = true imagePreviewSecond.isHidden = true imagePreviewThird.isHidden = true contentLength = 0 timestamp = nil note = nil } public func reLoad() { if let note = self.note { configure(note: note) } } func configure(note: Note) { self.note = note date.attributedText = NSAttributedString(string: getDate()) preview.textColor = UIColor.previewColor if note.isPublished() { pin.image = UIImage(systemName: "globe") pin.isHidden = false } else if note.isEncrypted() { let name = note.isUnlocked() ? "lock.open" : "lock" pin.contentMode = .scaleAspectFit pin.image = UIImage(systemName: name) pin.isHidden = false } else { pin.image = UIImage(systemName: "pin") pin.isHidden = !note.isPinned } pin.tintColor = UIColor.mainTheme let font = UIFont.systemFont(ofSize: CGFloat(UserDefaultsManagement.DefaultFontSize), weight: .semibold) let fontMetrics = UIFontMetrics(forTextStyle: .title1) let scaledFont = fontMetrics.scaledFont(for: font) title.font = scaledFont let dateFont = UIFont.systemFont(ofSize: CGFloat(UserDefaultsManagement.DefaultFontSize - 2), weight: .regular) let dateFontMetrics = UIFontMetrics(forTextStyle: .title3) let dateScaledFont = dateFontMetrics.scaledFont(for: dateFont) date.font = dateScaledFont let previewFont = UIFont.systemFont(ofSize: CGFloat(UserDefaultsManagement.DefaultFontSize - 2), weight: .regular) let previewFontMetrics = UIFontMetrics(forTextStyle: .title3) let previewScaledFont = previewFontMetrics.scaledFont(for: previewFont) preview.font = previewScaledFont } public func getDate() -> String { if let sort = note?.project.settings.sortBy, sort == .creationDate, let date = note?.getCreationDateForLabel() { return date } if let date = note?.getDateForLabel() { return date } return String() } public func reloadDate() { date.text = getDate() } public func updateView() { loadImagesPreview() if let note = self.note { attachHeaders(note: note) } reloadDate() } public func styleImageView(imageView: ImageView) { imageView.isHidden = false imageView.layer.borderWidth = 1 imageView.layer.borderColor = Color.darkGray.cgColor imageView.layer.cornerRadius = 4 imageView.clipsToBounds = true } public func attachHeaders(note: Note) { if let title = note.getTitle() { self.title.text = title self.preview.text = note.preview } else { self.title.text = String() self.preview.text = String() } } public func getPreviewImage(imageUrl: URL, note: Note) -> Image? { let tempURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("MainNotesList") if !FileManager.default.fileExists(atPath: tempURL.path) { try? FileManager.default.createDirectory(at: tempURL, withIntermediateDirectories: false, attributes: nil) } if let cacheName = imageUrl.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)?.md5 { let file = tempURL.appendingPathComponent(cacheName) if FileManager.default.fileExists(atPath: file.path) { if let data = try? Data(contentsOf: file), let image = UIImage(data: data) { return image } } do { let data = try Data(contentsOf: imageUrl) if let image = UIImage(data: data) { let size = CGRect(x: 0, y: 0, width: 70, height: 70) if let resized = image.resize(height: 70)?.croppedInRect(rect: size) { let jpegImageData = resized.jpegData(compressionQuality: 1) try? jpegImageData?.write(to: file, options: .atomic) return resized } } } catch { print(error.localizedDescription) } } return nil } public func fixTopConstraint(position: Int?, note: Note) { for constraint in self.contentView.constraints { if ["firstImageTop", "secondImageTop", "thirdImageTop"].contains(constraint.identifier) { let ident = constraint.identifier self.contentView.removeConstraint(constraint) let isPreviewExist = note.preview.trim().count > 0 var imageLink: UIImageView? switch constraint.identifier { case "firstImageTop": imageLink = self.imagePreview case "secondImageTop": imageLink = self.imagePreviewSecond case "thirdImageTop": imageLink = self.imagePreviewThird default: imageLink = self.imagePreview } guard let firstItem = imageLink else { continue } let secondItem = isPreviewExist ? self.preview : self.title let constr = NSLayoutConstraint(item: firstItem, attribute: .top, relatedBy: .equal, toItem: secondItem, attribute: .bottom, multiplier: 1, constant: 12) constr.identifier = ident self.contentView.addConstraint(constr) } } } } ================================================ FILE: FSNotes iOS/View/NotesTableView.swift ================================================ // // NotesTableView.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 1/29/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit import MobileCoreServices import AudioToolbox import SwipeCellKit import SSZipArchive class NotesTableView: UITableView, UITableViewDelegate, UITableViewDataSource, UITableViewDragDelegate { var notes = [Note]() var viewDelegate: ViewController? = nil public var selectedIndexPaths: [IndexPath]? func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return notes.count } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return calcHeight(indexPath: indexPath) } func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { return calcHeight(indexPath: indexPath) } func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { suggestedActions in let note = self.notes[indexPath.row] let menu = self.makeBulkMenu(editor: false, note: note) return menu } } func tableView(_ tableView: UITableView, shouldBeginMultipleSelectionInteractionAt indexPath: IndexPath) -> Bool { return true } private func calcHeight(indexPath: IndexPath) -> CGFloat { if notes.indices.contains(indexPath.row) { let note = notes[indexPath.row] if let urls = note.imageUrl, urls.count > 0 { if note.preview.count == 0 { if note.getTitle() != nil { // Title + image return 132 } // Images only return 120 } // Title + Prevew + Images return 160 } } return 75 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "noteCell", for: indexPath) as! NoteCellView cell.imageKeys = [] guard self.notes.indices.contains(indexPath.row) else { return cell } let note = self.notes[indexPath.row] if !note.isLoaded && !note.isLoadedFromCache { note.uiLoad() } cell.configure(note: note) cell.selectionStyle = .gray cell.loadImagesPreview(position: indexPath.row) cell.attachHeaders(note: note) return cell } func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { self.selectedIndexPaths = self.indexPathsForSelectedRows } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if isEditing { self.selectedIndexPaths = self.indexPathsForSelectedRows } guard !self.isEditing, notes.indices.contains(indexPath.row) else { return } var note = notes[indexPath.row] note.loadPreviewState() let evc = UIApplication.getEVC() if let editArea = evc.editArea, let u = editArea.undoManager { u.removeAllActions() } if note.container == .encryptedTextPack { viewDelegate?.unLock(notes: [note], completion: { notes in DispatchQueue.main.async { guard note.container != .encryptedTextPack else { self.askPasswordAndUnlock(note: note, indexPath: indexPath) return } self.reloadRows(notes: [note]) self.fill(note: note, indexPath: indexPath) } }) return } fill(note: note, indexPath: indexPath) if UserDefaultsManagement.autoVersioning && !note.hasGitRepository() { DispatchQueue.global().async { do { try note.saveRevision() } catch {/*_*/} } } } private func askPasswordAndUnlock(note: Note, indexPath: IndexPath) { self.viewDelegate?.unlockPasswordPrompt(completion: { password in self.viewDelegate?.unLock(notes: [note], completion: { success in if let success = success, success.count > 0 { self.reloadRows(notes: [note]) self.fill(note: note, indexPath: indexPath) } }, password: password) }) } private func askPasswordAndUnEncrypt(note: Note) { self.viewDelegate?.unlockPasswordPrompt(completion: { password in if note.container == .encryptedTextPack { let success = note.unEncrypt(password: password) note.password = nil if success { DispatchQueue.main.async { UIApplication.getEVC().refill() self.reloadRows(notes: [note], resetKeys: true) } } } }) } private func fill(note: Note, indexPath: IndexPath) { UIApplication.getVC().openEditorViewController() UIApplication.getEVC().fill(note: note, clearPreview: true) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.deselectRow(at: indexPath, animated: true) } } } func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { return true } func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { guard let vc = viewDelegate, !UserDefaultsManagement.sidebarIsOpened else { return nil } let note = self.notes[indexPath.row] // Delete let deleteAction = UIContextualAction(style: .destructive, title: NSLocalizedString("Delete", comment: "Table row action")) { [weak self] _, _, completion in guard let self = self else { return } self.viewDelegate?.sidebarTableView.removeTags(in: [note]) let isTrashed = note.isTrash() note.remove() self.removeRows(notes: [note]) if note.isEmpty() || isTrashed { vc.storage.removeBy(note: note) } completion(true) } deleteAction.image = UIImage(systemName: "trash") // Pin / Unpin let pinTitle = note.isPinned ? NSLocalizedString("Unpin", comment: "Table row action") : NSLocalizedString("Pin", comment: "Table row action") let pinAction = UIContextualAction(style: .normal, title: pinTitle) { [weak self] _, _, completion in guard let self = self, let cell = self.cellForRow(at: indexPath) as? NoteCellView else { completion(false) return } note.togglePin() cell.configure(note: note) let resorted = vc.storage.sortNotes(noteList: self.notes) guard let newIndex = resorted.firstIndex(of: note) else { completion(false) return } let newIndexPath = IndexPath(row: newIndex, section: 0) self.moveRow(at: indexPath, to: newIndexPath) self.notes = resorted self.reloadRows(at: [newIndexPath], with: .automatic) self.reloadRows(at: [indexPath], with: .automatic) completion(true) } pinAction.image = note.isPinned ? UIImage(systemName: "pin.slash") : UIImage(systemName: "pin") pinAction.backgroundColor = UIColor(red: 0.24, green: 0.59, blue: 0.94, alpha: 1.0) let config = UISwipeActionsConfiguration(actions: [deleteAction, pinAction]) config.performsFirstActionWithFullSwipe = true return config } public func turnOffEditing() { if self.isEditing { self.allowsMultipleSelectionDuringEditing = false self.setEditing(false, animated: true) deselectAllRows() } } public func getSelectedNotes() -> [Note] { var notes = [Note]() if let selectedRows = selectedIndexPaths { for indexPath in selectedRows { if self.notes.indices.contains(indexPath.row) { let note = self.notes[indexPath.row] notes.append(note) } } selectedIndexPaths = nil } return notes } public func deselectAllRows() { if let selected = indexPathsForSelectedRows { for indexP in selected { deselectRow(at: indexP, animated: false) } } } public func makeBulkMenu(editor: Bool = false, note: Note) -> UIMenu? { let handler: (_ action: UIAction) -> () = { action in switch action.identifier.rawValue { case "cancel": break case "delete": self.removeAction(notes: [note]) if editor { UIApplication.getEVC().cancel() } case "calendar": self.dateAction(notes: [note]) case "duplicate": self.duplicateAction(notes: [note]) case "move": self.moveAction(notes: [note]) case "commit": self.saveRevisionAction(note: note) case "history": self.historyAction(note: note) case "rename": self.renameAction(note: note) case "pinUnpin": if note.isPinned { note.removePin() self.removePins(notes: [note]) } else { note.addPin() self.addPins(notes: [note]) } case "lockUnlock": self.viewDelegate?.toggleNotesLock(notes: [note]) if editor { if !note.isUnlocked() { UIApplication.getEVC().cancel() } } case "removeEncryption": self.removeEncryption(note: note) case "copy": self.copyAction(note: note) case "share": self.shareAction(note: note) case "shareWeb": self.shareWebAction(note: note) case "deleteWeb": self.deleteWebAction(note: note) default: break } if ["pinUnpin", "removeEncryption"].contains(action.identifier.rawValue) { DispatchQueue.main.async { UIApplication.getEVC().configureNavMenu() } } self.turnOffEditing() } var actions = [UIAction]() let deleteTitle = NSLocalizedString("Delete", comment: "") actions.append(UIAction(title: deleteTitle, image: UIImage(systemName: "trash"), identifier: UIAction.Identifier("delete"), attributes: .destructive, handler: handler)) let calendarTitle = NSLocalizedString("Change Creation Date", comment: "") let calendarImage = UIImage(systemName: "calendar") actions.append(UIAction(title: calendarTitle, image: calendarImage, identifier: UIAction.Identifier("calendar"), handler: handler)) let duplicateTitle = NSLocalizedString("Duplicate", comment: "") let duplicateImage = UIImage(systemName: "doc.on.doc") actions.append(UIAction(title: duplicateTitle, image: duplicateImage, identifier: UIAction.Identifier("duplicate"), handler: handler)) let moveTitle = NSLocalizedString("Move", comment: "") let moveImage = UIImage(systemName: "folder") actions.append(UIAction(title: moveTitle, image: moveImage, identifier: UIAction.Identifier("move"), handler: handler)) if note.hasGitRepository() && !note.isEncrypted() { let commitTitle = NSLocalizedString("Save Revision", comment: "") let commitImage = UIImage(systemName: "plus.circle") actions.append(UIAction(title: commitTitle, image: commitImage, identifier: UIAction.Identifier("commit"), handler: handler)) } if UserDefaultsManagement.autoVersioning && !note.isEncrypted() { let historyTitle = NSLocalizedString("History", comment: "") let historyImage = UIImage(systemName: "clock.arrow.circlepath") actions.append(UIAction(title: historyTitle, image: historyImage, identifier: UIAction.Identifier("history"), handler: handler)) } let renameTitle = NSLocalizedString("Rename", comment: "") let renameImage = UIImage(systemName: "pencil.circle") actions.append(UIAction(title: renameTitle, image: renameImage, identifier: UIAction.Identifier("rename"), handler: handler)) let pinUnpinTitle = note.isPinned ? NSLocalizedString("Unpin", comment: "") : NSLocalizedString("Pin", comment: "") let pinUnpinImage = UIImage(systemName: note.isPinned ? "pin.slash" : "pin") actions.append(UIAction(title: pinUnpinTitle, image: pinUnpinImage, identifier: UIAction.Identifier("pinUnpin"), handler: handler)) let lockUnlockTitle = (note.isUnlocked() && note.isEncrypted()) || !note.isEncrypted() ? NSLocalizedString("Lock", comment: "") : NSLocalizedString("Unlock", comment: "") let lockUnlockImageName = (note.isUnlocked() && note.isEncrypted()) || !note.isEncrypted() ? "lock" : "lock.open" let lockUnlockImage = UIImage(systemName: lockUnlockImageName) actions.append(UIAction(title: lockUnlockTitle, image: lockUnlockImage, identifier: UIAction.Identifier("lockUnlock"), handler: handler)) if note.isEncrypted() && !note.project.isEncrypted { let removeEncryptionTitle = NSLocalizedString("Remove Encryption", comment: "") let removeEncryptionImage = UIImage(systemName: "lock.slash") actions.append(UIAction(title: removeEncryptionTitle, image: removeEncryptionImage, identifier: UIAction.Identifier("removeEncryption"), handler: handler)) } var clipboardName = "doc.on.clipboard" if #available(iOS 16.0, *) { clipboardName = "clipboard" } let copyTitle = NSLocalizedString("Copy Plain Text", comment: "") let copyImage = UIImage(systemName: clipboardName) actions.append(UIAction(title: copyTitle, image: copyImage, identifier: UIAction.Identifier("copy"), handler: handler)) let shareTitle = NSLocalizedString("Share", comment: "") let shareImage = UIImage(systemName: "square.and.arrow.up") actions.append(UIAction(title: shareTitle, image: shareImage, identifier: UIAction.Identifier("share"), handler: handler)) var shareWebTitle = NSLocalizedString("Create Web Page", comment: "") if note.apiId != nil { shareWebTitle = NSLocalizedString("Update Web Page", comment: "") } let shareWebImage = UIImage(systemName: "newspaper") actions.append(UIAction(title: shareWebTitle, image: shareWebImage, identifier: UIAction.Identifier("shareWeb"), handler: handler)) if note.apiId != nil { let deleteWebTitle = NSLocalizedString("Delete Web Page", comment: "") let deleteWebImage = UIImage(systemName: "newspaper.fill") actions.append(UIAction(title: deleteWebTitle, image: deleteWebImage, identifier: UIAction.Identifier("deleteWeb"), handler: handler)) } return UIMenu(title: note.getShortTitle(), children: actions) } func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool { setContentOffset(CGPoint(x: 0, y: -44), animated: true) return false } func scrollViewDidScroll(_ scrollView: UIScrollView) { viewDelegate?.navigationItem.hidesSearchBarWhenScrolling = false viewDelegate?.navigationItem.largeTitleDisplayMode = .automatic } public func actionsSheet(notes: [Note], showAll: Bool = false, presentController: UIViewController, back: Bool = false) { let note = notes.first! let actionSheet = UIAlertController(title: note.project.getFullLabel() + " ➔ " + note.url.lastPathComponent, message: nil, preferredStyle: .actionSheet) let remove = UIAlertAction(title: NSLocalizedString("Delete", comment: ""), style: .destructive, handler: { _ in self.turnOffEditing() self.removeAction(notes: notes) if presentController.isKind(of: EditorViewController.self) || back { UIApplication.getEVC().cancel() } }) remove.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "trash")?.resize(maxWidthHeight: 23) { remove.setValue(image, forKey: "image") } actionSheet.addAction(remove) if showAll && note.hasGitRepository() && !note.isEncrypted() { let history = UIAlertAction(title: NSLocalizedString("Save Revision", comment: ""), style: .default, handler: { _ in self.saveRevisionAction(note: notes.first!) }) history.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "plus.circle")?.resize(maxWidthHeight: 23) { history.setValue(image, forKey: "image") } actionSheet.addAction(history) } if showAll && UserDefaultsManagement.autoVersioning && !note.isEncrypted() { let history = UIAlertAction(title: NSLocalizedString("History", comment: ""), style: .default, handler: { _ in self.historyAction(note: notes.first!) }) history.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "clock.arrow.circlepath")?.resize(maxWidthHeight: 23) { history.setValue(image, forKey: "image") } actionSheet.addAction(history) } let creationDate = UIAlertAction(title: NSLocalizedString("Change Creation Date", comment: ""), style: .default, handler: { _ in self.dateAction(notes: notes) }) creationDate.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "calendar")?.resize(maxWidthHeight: 23) { creationDate.setValue(image, forKey: "image") } actionSheet.addAction(creationDate) let duplicate = UIAlertAction(title: NSLocalizedString("Duplicate", comment: ""), style: .default, handler: { _ in self.duplicateAction(notes: notes) }) duplicate.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "doc.on.doc")?.resize(maxWidthHeight: 23) { duplicate.setValue(image, forKey: "image") } actionSheet.addAction(duplicate) if showAll { let rename = UIAlertAction(title: NSLocalizedString("Rename", comment: ""), style: .default, handler: { _ in self.renameAction(note: note) }) rename.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "pencil.circle")?.resize(maxWidthHeight: 23) { rename.setValue(image, forKey: "image") } actionSheet.addAction(rename) let title = note.isPinned ? NSLocalizedString("Unpin", comment: "") : NSLocalizedString("Pin", comment: "") let pin = UIAlertAction(title: title, style: .default, handler: { _ in if note.isPinned { note.removePin() self.removePins(notes: [note]) } else { note.addPin() self.addPins(notes: [note]) } }) pin.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: note.isPinned ? "pin.slash" : "pin")?.resize(maxWidthHeight: 23) { pin.setValue(image, forKey: "image") } actionSheet.addAction(pin) } let move = UIAlertAction(title: NSLocalizedString("Move", comment: ""), style: .default, handler: { _ in self.turnOffEditing() self.moveAction(notes: notes) }) move.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "move.3d")?.resize(maxWidthHeight: 23) { move.setValue(image, forKey: "image") } actionSheet.addAction(move) if showAll { let alertTitle = (note.isUnlocked() && note.isEncrypted()) || !note.isEncrypted() ? NSLocalizedString("Lock", comment: "") : NSLocalizedString("Unlock", comment: "") let imageName = (note.isUnlocked() && note.isEncrypted()) || !note.isEncrypted() ? "lock" : "lock.open" let encryption = UIAlertAction(title: alertTitle, style: .default, handler: { _ in self.viewDelegate?.toggleNotesLock(notes: [note]) if !note.isUnlocked(), presentController.isKind(of: EditorViewController.self) || back { UIApplication.getEVC().cancel() } }) encryption.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: imageName)?.resize(maxWidthHeight: 23) { encryption.setValue(image, forKey: "image") } actionSheet.addAction(encryption) if note.isEncrypted() { let removeEncryption = UIAlertAction(title: NSLocalizedString("Remove Encryption", comment: ""), style: .default, handler: { _ in self.removeEncryption(note: note) }) removeEncryption.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "lock.slash")?.resize(maxWidthHeight: 23) { removeEncryption.setValue(image, forKey: "image") } actionSheet.addAction(removeEncryption) } var clipboardName = "doc.on.clipboard" if #available(iOS 16.0, *) { clipboardName = "clipboard" } let copy = UIAlertAction(title: NSLocalizedString("Copy Plain Text", comment: ""), style: .default, handler: { _ in self.copyAction(note: note) }) copy.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: clipboardName)?.resize(maxWidthHeight: 23) { copy.setValue(image, forKey: "image") } actionSheet.addAction(copy) let share = UIAlertAction(title: NSLocalizedString("Share", comment: ""), style: .default, handler: { _ in self.shareAction(note: note) }) share.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "square.and.arrow.up")?.resize(maxWidthHeight: 23) { share.setValue(image, forKey: "image") } actionSheet.addAction(share) } let dismiss = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: { _ in if self.isEditing { self.setEditing(false, animated: true) } }) actionSheet.addAction(dismiss) if let view = UIApplication.getEVC().view { actionSheet.popoverPresentationController?.sourceView = view actionSheet.popoverPresentationController?.sourceRect = CGRect(x: view.bounds.size.width / 2.0, y: view.bounds.size.height, width: 2.0, height: 1.0) } presentController.present(actionSheet, animated: true, completion: nil) } public func removeRows(notes: [Note]) { guard notes.count > 0, let vc = viewDelegate, vc.isNoteInsertionAllowed() else { return } vc.removeSpotlightIndex(notes: notes) var indexPaths = [IndexPath]() var tags = [String]() for note in notes { if let i = self.notes.firstIndex(where: { $0 === note }) { indexPaths.append(IndexPath(row: i, section: 0)) tags.append(contentsOf: note.tags) } } beginUpdates() self.notes.removeAll(where: { notes.contains($0) }) deleteRows(at: indexPaths, with: .automatic) endUpdates() vc.updateNotesCounter() vc.sidebarTableView.delete(tags: tags) } public func insertRows(notes: [Note]) { guard notes.count > 0, let vc = viewDelegate, vc.isNoteInsertionAllowed() else { return } vc.storage.loadPins(notes: notes) var toInsert = [Note]() for note in notes { guard vc.storage.searchQuery.isFit(note: note), !self.notes.contains(where: { $0 === note }) else { continue } toInsert.append(note) } guard toInsert.count > 0 else { return } vc.updateSpotlightIndex(notes: toInsert) let nonSorted = self.notes + toInsert let sorted = vc.storage.sortNotes(noteList: nonSorted) var indexPaths = [IndexPath]() for note in toInsert { guard let index = sorted.firstIndex(where: { $0 === note }) else { continue } indexPaths.append(IndexPath(row: index, section: 0)) } self.notes = sorted beginUpdates() insertRows(at: indexPaths, with: .fade) endUpdates() } public func reloadRows(notes: [Note], resetKeys: Bool = false) { beginUpdates() for note in notes { if let row = self.notes.firstIndex(where: { $0 === note }) { let indexPath = IndexPath(row: row, section: 0) if let cell = cellForRow(at: indexPath) as? NoteCellView { if resetKeys { cell.imageKeys = [] } cell.configure(note: note) cell.updateView() } } } endUpdates() viewDelegate?.updateSpotlightIndex(notes: notes) } public func reloadRowForce(note: Note) { note.invalidateCache() note.loadPreviewInfo() if let index = notes.firstIndex(of: note) { reloadRows(at: [IndexPath(row: index, section: 0)], with: .automatic) } } private func renameAction(note: Note) { let alertController = UIAlertController(title: NSLocalizedString("Rename note:", comment: ""), message: nil, preferredStyle: .alert) alertController.addTextField(configurationHandler: { [] (textField: UITextField) in textField.placeholder = NSLocalizedString("Enter note name", comment: "") textField.attributedText = NSAttributedString(string: note.getFileName()) }) let confirmAction = UIAlertAction(title: "OK", style: .default) { (_) in guard let name = alertController.textFields?[0].text, name.count > 0 else { return } self.rename(note: note, to: name) } let title = NSLocalizedString("Cancel", comment: "") let cancelAction = UIAlertAction(title: title, style: .cancel) { (_) in } alertController.addAction(confirmAction) alertController.addAction(cancelAction) UIApplication.getNC()?.present(alertController, animated: true) { alertController.textFields![0].selectAll(nil) } } public func rename(note: Note, to name: String) { guard name.count > 0, name.trim().count > 0 else { return } var name = name var i = 1 while note.project.fileExistCaseInsensitive(fileName: name, ext: note.url.pathExtension) { // disables renaming loop if note.fileName.startsWith(string: name) { return } let items = name.split(separator: " ") if let last = items.last, let position = Int(last) { let full = items.dropLast() name = full.joined(separator: " ") + " " + String(position + 1) i = position + 1 } else { name = name + " " + String(i) i += 1 } } let isPinned = note.isPinned let dst = note.getNewURL(name: name) let src = note.url note.removePin() if note.isEncrypted() { _ = note.lock() } if note.move(to: dst) { note.url = dst note.parseURL() note.moveHistory(src: src, dst: dst) } if isPinned { note.addPin() } DispatchQueue.main.async { self.reloadRows(notes: [note]) } } public func removeAction(notes: [Note]) { guard let vc = viewDelegate else { return } vc.sidebarTableView.removeTags(in: notes) for note in notes { note.remove() } removeRows(notes: notes) allowsMultipleSelectionDuringEditing = false setEditing(false, animated: true) } public func moveAction(notes: [Note]) { let moveController = MoveViewController(notes: notes, notesTableView: self) let controller = UINavigationController(rootViewController: moveController) let nvc = UIApplication.getNC() nvc?.present(controller, animated: true, completion: nil) } public func dateAction(notes: [Note]) { let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil) let datePickerViewController = storyBoard.instantiateViewController(withIdentifier: "datePickerViewController") as! DatePickerViewController datePickerViewController.notes = notes let nvc = UIApplication.getNC() nvc?.present(datePickerViewController, animated: true ) } public func showLoader() { let alert = UIAlertController(title: nil, message: " ", preferredStyle: .alert) let loadingIndicator = UIActivityIndicatorView(style: .large) loadingIndicator.translatesAutoresizingMaskIntoConstraints = false loadingIndicator.startAnimating() let label = UILabel() label.text = NSLocalizedString("Loading...", comment: "") label.font = UIFont.preferredFont(forTextStyle: .title2) label.adjustsFontForContentSizeCategory = true label.numberOfLines = 1 label.translatesAutoresizingMaskIntoConstraints = false let stack = UIStackView(arrangedSubviews: [loadingIndicator, label]) stack.axis = .horizontal stack.spacing = 14 stack.alignment = .center stack.translatesAutoresizingMaskIntoConstraints = false alert.view.addSubview(stack) NSLayoutConstraint.activate([ stack.centerXAnchor.constraint(equalTo: alert.view.centerXAnchor), stack.centerYAnchor.constraint(equalTo: alert.view.centerYAnchor, constant: 6), stack.leadingAnchor.constraint(greaterThanOrEqualTo: alert.view.leadingAnchor, constant: 20), stack.trailingAnchor.constraint(lessThanOrEqualTo: alert.view.trailingAnchor, constant: -20), ]) UIApplication.getNC()?.present(alert, animated: true) } public func hideLoader() { DispatchQueue.main.async { UIApplication.getNC()?.dismiss(animated: false, completion: nil) } } public func saveRevisionAction(note: Note? = nil, project: Project? = nil) { var current: Project? if let unwrappedProject = project { current = unwrappedProject } else if let note = note { current = note.getGitProject() } guard let project = current else { return } guard let nvc = UIApplication.getNC() else { return } let viewController = UIApplication.getVC() // Show loader let title = NSLocalizedString("Loading...", comment: "") let alert = UIAlertController(title: nil, message: title, preferredStyle: .alert) let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50)) loadingIndicator.hidesWhenStopped = true loadingIndicator.style = UIActivityIndicatorView.Style.medium loadingIndicator.startAnimating(); alert.view.addSubview(loadingIndicator) nvc.present(alert, animated: true) DispatchQueue.global(qos: .userInitiated).async { do { try project.saveRevision() // Hide loader DispatchQueue.main.async { nvc.dismiss(animated: false, completion: nil) } } catch GitError.noAddedFiles { DispatchQueue.main.async { // Hide loader nvc.dismiss(animated: false, completion: nil) let alert = UIAlertController(title: "No changes", message: "Nothing new to commit", preferredStyle: UIAlertController.Style.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)) nvc.present(alert, animated: true, completion: nil) } } catch { DispatchQueue.main.async { // Hide loader nvc.dismiss(animated: false, completion: nil) project.gitStatus = error.localizedDescription let alert = UIAlertController(title: "Git error", message: error.localizedDescription, preferredStyle: UIAlertController.Style.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)) nvc.present(alert, animated: true, completion: nil) } return } if project.isGitOriginExist() { viewController.gitQueue.addOperation({ try? project.pull() try? project.push() }) } } } private func historyAction(note: Note) { let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil) let datePickerViewController = storyBoard.instantiateViewController(withIdentifier: "revisionsViewController") as! RevisionsViewController datePickerViewController.note = note UIApplication.getNC()?.present(datePickerViewController, animated: true) } private func copyAction(note: Note) { let item = [kUTTypeUTF8PlainText as String : note.content.string as Any] UIPasteboard.general.items = [item] } public func shareAction(note: Note, isHTML: Bool = false) { AudioServicesPlaySystemSound(1519) var tempURL = note.url if note.isTextBundle() { tempURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(note.getName()).zip") SSZipArchive.createZipFile(atPath: tempURL.path, withContentsOfDirectory: note.url.path, keepParentDirectory: true) } let objectsToShare = [tempURL] as [Any] let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil) activityVC.excludedActivityTypes = [ UIActivity.ActivityType.addToReadingList ] guard let presentController = UIApplication.getNC() else { return } presentController.present(activityVC, animated: true, completion: nil) guard let popOver = activityVC.popoverPresentationController else { return } popOver.permittedArrowDirections = .up let notesTable = UIApplication.getVC().notesTable let editorView = UIApplication.getEVC().editArea if let topViewController = presentController.topViewController { if topViewController.isKind(of: EditorViewController.self) { popOver.sourceView = editorView popOver.sourceRect = CGRect(x: editorView!.bounds.midX, y: 80, width: 0, height: 0) } else if topViewController.isKind(of: ViewController.self), let i = notesTable?.notes.firstIndex(where: {$0 === note}), let rowView = notesTable?.cellForRow(at: IndexPath(row: i, section: 0)) { popOver.sourceView = rowView popOver.sourceRect = CGRect(x: notesTable!.bounds.midX, y: rowView.frame.height, width: 10, height: 10) } } } public func duplicateAction(notes: [Note]) { var dupes = [Note]() for note in notes { let src = note.url let dst = NameHelper.generateCopy(file: note.url) if note.isTextBundle() || note.isEncrypted() { try? FileManager.default.copyItem(at: src, to: dst) let noteDupe = Note(url: dst, with: note.project) noteDupe.load() viewDelegate?.storage.add(noteDupe) dupes.append(noteDupe) continue } let name = dst.deletingPathExtension().lastPathComponent let noteDupe = Note(name: name, project: note.project, type: note.type, cont: note.container) noteDupe.content = NSMutableAttributedString(string: note.content.string) // Clone images if note.type == .Markdown && note.container == .none { let images = note.content.getImagesAndFiles() for image in images { noteDupe.move(from: image.url, imagePath: image.path, to: note.project, copy: true) } } if noteDupe.save() { Storage.shared().add(noteDupe) } dupes.append(noteDupe) } insertRows(notes: dupes) if let scrollTo = dupes.first { viewDelegate?.notesTable.scrollTo(note: scrollTo) } } private func decryptUnlocked(notes: [Note]) -> [Note] { var notes = notes var toReload = [Note]() for note in notes { if note.isUnlocked() { if note.unEncryptUnlocked() { notes.removeAll { $0 === note } toReload.append(note) note.invalidateCache() } } } DispatchQueue.main.async { self.reloadRows(notes: toReload, resetKeys: true) } return notes } public func removeEncryption(note: Note) { let vc = UIApplication.getVC() let notes = decryptUnlocked(notes: [note]) guard let note = notes.first else { return } vc.getMasterPassword() { password in if note.container == .encryptedTextPack { let success = note.unEncrypt(password: password) note.password = nil if success { DispatchQueue.main.async { UIApplication.getEVC().refill() } } else { self.askPasswordAndUnEncrypt(note: note) return } } DispatchQueue.main.async { self.reloadRows(notes: notes, resetKeys: true) } } } public func shareWebAction(note: Note) { UIApplication.getVC().createAPI(note: note, completion: { url in DispatchQueue.main.async { self.reloadRowForce(note: note) if let url = url { UIApplication.shared.open(url) } UIApplication.getEVC().configureNavMenu() } }) } public func deleteWebAction(note: Note) { UIApplication.getVC().deleteAPI(note: note, completion: { DispatchQueue.main.async { self.reloadRowForce(note: note) UIApplication.getEVC().configureNavMenu() } }) } public func moveRowUp(note: Note) { guard let vc = viewDelegate, vc.isNoteInsertionAllowed(), vc.storage.searchQuery.isFit(note: note) else { return } guard let currentIndex = notes.firstIndex(where: { $0 === note }) else { return } let sorted = vc.storage.sortNotes(noteList: notes) guard let targetIndex = sorted.firstIndex(where: { $0 === note }) else { return } guard currentIndex != targetIndex else { return } self.notes = sorted let from = IndexPath(row: currentIndex, section: 0) let to = IndexPath(row: targetIndex, section: 0) beginUpdates() moveRow(at: from, to: to) endUpdates() } @objc public func toggleSelectAll() { guard self.isEditing else { return } if let selected = self.indexPathsForSelectedRows, (selected.count - 1) == self.notes.count { for indexPath in selected { self.deselectRow(at: indexPath, animated: false) } self.selectedIndexPaths = nil } else { for i in 0...notes.count { self.selectRow(at: IndexPath(item: i, section: 0), animated: false, scrollPosition: .none) } self.selectedIndexPaths = indexPathsForSelectedRows } } private func invalidPasswordAlert() { let invalid = NSLocalizedString("Invalid Password", comment: "") let message = NSLocalizedString("Please enter valid password", comment: "") let alert = UIAlertController(title: invalid, message: message, preferredStyle: UIAlertController.Style.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)) UIApplication.getVC().present(alert, animated: true, completion: nil) } func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] { guard let cell = tableView.cellForRow(at: indexPath) as? NoteCellView, let url = cell.note?.url else { return [] } let itemProvider = NSItemProvider(item: url as NSSecureCoding, typeIdentifier: kUTTypeURL as String) return [UIDragItem(itemProvider: itemProvider)] } public func addPins(notes pinned: [Note]) { guard let vc = viewDelegate else { return } let oldNotes = self.notes let newNotes = vc.storage.sortNotes(noteList: oldNotes) self.notes = newNotes var rowsToReload: [IndexPath] = [] beginUpdates() for note in pinned { guard let from = oldNotes.firstIndex(of: note), let to = newNotes.firstIndex(of: note), from != to else { continue } moveRow( at: IndexPath(row: from, section: 0), to: IndexPath(row: to, section: 0) ) rowsToReload.append(IndexPath(row: to, section: 0)) } endUpdates() if !rowsToReload.isEmpty { reloadRows(at: rowsToReload, with: .none) } } public func removePins(notes unpinned: [Note]) { guard let vc = viewDelegate else { return } let oldNotes = self.notes let newNotes = vc.storage.sortNotes(noteList: oldNotes) self.notes = newNotes var rowsToReload: [IndexPath] = [] beginUpdates() for note in unpinned { guard let from = oldNotes.firstIndex(of: note), let to = newNotes.firstIndex(of: note), from != to else { continue } moveRow( at: IndexPath(row: from, section: 0), to: IndexPath(row: to, section: 0) ) rowsToReload.append(IndexPath(row: to, section: 0)) } endUpdates() if !rowsToReload.isEmpty { reloadRows(at: rowsToReload, with: .none) } } public func scrollTo(note: Note) { if let index = notes.firstIndex(of: note) { let indexPath = IndexPath(row: index, section: 0) scrollToRow(at: indexPath, at: .top, animated: true) } } public func doVisualChanges(results: ([Note], [Note], [Note])) { guard results.0.count > 0 || results.1.count > 0 || results.2.count > 0 else { return } DispatchQueue.main.async { self.removeRows(notes: results.0) self.insertRows(notes: results.1) self.reloadRows(notes: results.2) } } } ================================================ FILE: FSNotes iOS/View/SidebarTableCellView.swift ================================================ // // SidebarTableCellView.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 5/5/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit class SidebarTableCellView: UITableViewCell { @IBOutlet weak var icon: UIImageView! @IBOutlet weak var label: UILabel! @IBOutlet weak var labelConstraint: NSLayoutConstraint! public var sidebarItem: SidebarItem? func configure(sidebarItem: SidebarItem) { self.sidebarItem = sidebarItem self.icon.constraints[1].constant = 21 self.labelConstraint.constant = 11 icon.image = sidebarItem.icon var font = UIFont.systemFont(ofSize: 15) if sidebarItem.type == .Project || sidebarItem.type == .ProjectEncryptedLocked || sidebarItem.type == .ProjectEncryptedUnlocked || sidebarItem.type == .Tag { font = UIFont.systemFont(ofSize: 14) } let fontMetrics = UIFontMetrics(forTextStyle: .title3) font = fontMetrics.scaledFont(for: font) label.font = font label.text = sidebarItem.name } override func layoutSubviews() { super.layoutSubviews() self.selectedBackgroundView?.backgroundColor = UIColor.currentSidebarCell self.selectedBackgroundView?.frame = CGRect(x: 0, y: 0, width: 5, height: 40) } } ================================================ FILE: FSNotes iOS/View/SidebarTableView.swift ================================================ // // SidebarTableView.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 5/5/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation import UIKit import AudioToolbox class SidebarTableView: UITableView, UITableViewDelegate, UITableViewDataSource, UITableViewDropDelegate { public var sidebar = Sidebar() private var busyTrashReloading = false public var viewController: ViewController? func numberOfSections(in tableView: UITableView) -> Int { return sidebar.items.count } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return sidebar.items[section].count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "sidebarCell", for: indexPath) as! SidebarTableCellView guard sidebar.items.indices.contains(indexPath.section), sidebar.items[indexPath.section].indices.contains(indexPath.row) else { return cell } let sidebarItem = sidebar.items[indexPath.section][indexPath.row] cell.configure(sidebarItem: sidebarItem) cell.contentView.setNeedsLayout() cell.contentView.layoutIfNeeded() return cell } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { if section == 0 { return UIView() } return nil } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { if section == 0 { return 5 } if section == 1 && UIApplication.getVC().storage.getNonSystemProjects().count == 0 { return 0 } return 25 } func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { return 0 } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 37 } func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { selectRow(at: indexPath, animated: false, scrollPosition: .none) self.tableView(tableView, didSelectRowAt: indexPath) } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let vc = self.viewController else { return } let selectedSection = SidebarSection(rawValue: indexPath.section) guard sidebar.items.indices.contains(indexPath.section) && sidebar.items[indexPath.section].indices.contains(indexPath.row) else { return } let sidebarItem = sidebar.items[indexPath.section][indexPath.row] guard vc.storage.searchQuery.projects.first != sidebarItem.project || sidebarItem.type == .Tag else { return } if let project = vc.storage.searchQuery.projects.first, getIndexPathBy(project: project) == indexPath, vc.notesTable.isEditing { vc.notesTable.toggleSelectAll() return } if let project = sidebarItem.project, project.isLocked() { vc.enableLockedProject() } else { vc.disableLockedProject() } guard sidebar.items.indices.contains(indexPath.section) && sidebar.items[indexPath.section].indices.contains(indexPath.row) else { return } vc.notesTable.turnOffEditing() var name = sidebarItem.name if sidebarItem.type == .Tag { name = "#\(name)" } if selectedSection == .Tags { deselectAllTags() } else { deselectAllProjects() deselectAllTags() } selectRow(at: indexPath, animated: false, scrollPosition: .none) vc.configureNavMenu(for: sidebarItem) vc.navigationItem.searchController?.searchBar.text = "" // Save last state if sidebarItem.isSystem() { UserDefaultsManagement.lastSidebarItem = indexPath.row UserDefaultsManagement.lastProjectURL = nil } else if let project = sidebarItem.project, !project.isVirtual { UserDefaultsManagement.lastSidebarItem = nil UserDefaultsManagement.lastProjectURL = project.url } vc.buildSearchQuery() vc.reloadNotesTable() { DispatchQueue.main.async { vc.notesTable.hideLoader() vc.setNavTitle(folder: name) vc.isLoadedSidebar = true guard vc.notesTable.notes.count > 0 else { self.unloadAllTags() return } let path = IndexPath(row: 0, section: 0) vc.notesTable.scrollToRow(at: path, at: .top, animated: true) if selectedSection != .Tags { self.loadAllTags() vc.resizeSidebar(withAnimation: true) } } } } func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { suggestedActions in let sidebarItem = self.sidebar.items[indexPath.section][indexPath.row] let menu = self.viewController!.makeSidebarSettingsMenu(for: sidebarItem) return menu } } public func selectCurrentProject() { guard let vc = self.viewController else { return } var indexPath: IndexPath = IndexPath(row: 0, section: 0) if let type = vc.storage.searchQuery.type, let ip = getIndexPathBy(type: type) { indexPath = ip } else if let project = vc.storage.searchQuery.projects.first, let ip = getIndexPathBy(project: project) { indexPath = ip } let sidebarItem = sidebar.items[indexPath.section][indexPath.row] let name = sidebarItem.name selectRow(at: indexPath, animated: false, scrollPosition: .none) vc.configureNavMenu(for: sidebarItem) vc.buildSearchQuery() vc.reloadNotesTable() { DispatchQueue.main.async { vc.setNavTitle(folder: name) } } } private func deselectAllTags() { if let selectedIndexPaths = indexPathsForSelectedRows { for indexPathItem in selectedIndexPaths { if indexPathItem.section == SidebarSection.Tags.rawValue { deselectRow(at: indexPathItem, animated: false) } } } } private func deselectAllProjects() { if let selectedIndexPaths = indexPathsForSelectedRows { for indexPathItem in selectedIndexPaths { if indexPathItem.section == SidebarSection.Projects.rawValue || indexPathItem.section == SidebarSection.System.rawValue { deselectRow(at: indexPathItem, animated: false) } } } } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { cell.backgroundColor = UIColor.clear } public func deselectAll() { if let paths = indexPathsForSelectedRows { for path in paths { deselectRow(at: path, animated: false) } } } public func getSidebarItem(project: Project? = nil) -> SidebarItem? { if let project = project, sidebar.items.count > 1 { return sidebar.items[1].first(where: { $0.project == project }) } guard let indexPath = indexPathForSelectedRow else { return nil } let item = sidebar.items[indexPath.section][indexPath.row] return item } func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) { guard let vc = viewController else { return } guard let indexPath = coordinator.destinationIndexPath, let cell = tableView.cellForRow(at: indexPath) as? SidebarTableCellView else { return } guard let sidebarItem = cell.sidebarItem else { return } _ = coordinator.session.loadObjects(ofClass: URL.self) { item in let pathList = item as [URL] for url in pathList { guard let note = Storage.shared().getBy(url: url) else { continue } switch sidebarItem.type { case .Project, .Inbox: guard let project = sidebarItem.project else { break } self.move(note: note, in: project) case .Trash: note.remove() vc.notesTable.removeRows(notes: [note]) default: break } } vc.notesTable.isEditing = false vc.navigationController?.setToolbarHidden(true, animated: true) } } func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal { guard let indexPath = destinationIndexPath, let cell = tableView.cellForRow(at: indexPath) as? SidebarTableCellView, let sidebarItem = cell.sidebarItem else { return UITableViewDropProposal(operation: .cancel) } if sidebarItem.project != nil || sidebarItem.type == .Trash { return UITableViewDropProposal(operation: .copy) } return UITableViewDropProposal(operation: .cancel) } private func move(note: Note, in project: Project) { guard let vc = viewController else { return } let dstURL = project.url.appendingPathComponent(note.name) if note.project != project { note.moveImages(to: project) if note.isEncrypted() { _ = note.lock() } guard note.move(to: dstURL) else { let alert = UIAlertController(title: "Oops 👮‍♂️", message: "File with this name already exist", preferredStyle: UIAlertController.Style.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)) vc.present(alert, animated: true, completion: nil) note.moveImages(to: note.project) return } note.moveHistory(src: note.url, dst: dstURL) note.url = dstURL note.parseURL() note.project = project // resets tags in sidebar removeTags(in: [note]) // reload tags (in remove tags operation non fitted) _ = note.scanContentTags() vc.notesTable.removeRows(notes: [note]) vc.notesTable.insertRows(notes: [note]) } } public func getSidebarProjects() -> [Project]? { guard let indexPaths = UIApplication.getVC().sidebarTableView?.indexPathsForSelectedRows else { return nil } var projects = [Project]() for indexPath in indexPaths { let item = sidebar.items[indexPath.section][indexPath.row] if let project = item.project { projects.append(project) } } if projects.count > 0 { return projects } if let root = Storage.shared().getDefault() { return [root] } return nil } public func getAllTags(projects: [Project]? = nil) -> [String] { var tags = [String]() if let projects = projects { for project in projects { let projectTags = project.getAllTags() for tag in projectTags { if !tags.contains(tag) { tags.append(tag) } } } } return tags.sorted() } private func getAllTags(notes: [Note]? = nil) -> [String] { var tags = [String]() if let notes = notes { for note in notes { for tag in note.tags { if !tags.contains(tag) { tags.append(tag) } } } } return tags.sorted() } public func loadAllTags() { guard UserDefaultsManagement.inlineTags, let vc = viewController else { return } unloadAllTags() let notes = vc.notesTable.notes let tags = getAllTags(notes: notes) guard tags.count > 0, self.sidebar.items.indices.contains(2) else { return } var indexPaths = [IndexPath]() for tag in tags { let position = self.sidebar.items[2].count let element = SidebarItem(name: tag, type: .Tag) self.sidebar.items[2].insert(element, at: position) indexPaths.append(IndexPath(row: position, section: 2)) } insertRows(at: indexPaths, with: .automatic) } public func unloadAllTags() { guard sidebar.items.indices.contains(2), sidebar.items[2].count > 0 else { return } let rows = numberOfRows(inSection: 2) if rows > 0 { self.sidebar.items[2].removeAll() var indexPaths = [IndexPath]() for index in stride(from: rows - 1, to: -1, by: -1) { indexPaths.append(IndexPath(row: index, section: 2)) } deleteRows(at: indexPaths, with: .automatic) } } public func removeTags(in notes: [Note]) { for note in notes { note.tags.removeAll() } loadTags(notes: notes) } public func loadTags(notes: [Note]) { var toInsert = [String]() var toDelete = [String]() for note in notes { guard let query = createQueryWithoutTags(), query.isFit(note: note) else { continue } let result = note.scanContentTags() if result.0.count > 0 { toInsert.append(contentsOf: result.0) } if result.1.count > 0 { toDelete.append(contentsOf: result.1) note.tags.removeAll(where: { result.1.contains($0) }) } } toInsert = Array(Set(toInsert)) toDelete = Array(Set(toDelete)) insert(tags: toInsert) delete(tags: toDelete) } public func insert(tags: [String]) { let currentTags = sidebar.items[2].compactMap({ $0.name }) var toInsert = [String]() for tag in tags { if currentTags.contains(tag) { continue } toInsert.append(tag) } guard toInsert.count > 0 else { return } let nonSorted = currentTags + toInsert let sorted = nonSorted.sorted() var indexPaths = [IndexPath]() for tag in toInsert { guard let index = sorted.firstIndex(of: tag) else { continue } indexPaths.append(IndexPath(row: index, section: 2)) } sidebar.items[2] = sorted.compactMap({ SidebarItem(name: $0, type: .Tag) }) insertRows(at: indexPaths, with: .fade) } public func delete(tags: [String]) { guard let vc = viewController else { return } var allTags = [String]() if let project = vc.storage.searchQuery.projects.first { allTags = project.getAllTags() } else if let type = vc.storage.searchQuery.type { var notes = [Note]() switch type { case .All: notes = Storage.shared().noteList break case .Inbox: notes = Storage.shared().noteList.filter({ $0.project.isDefault }) break case .Todo: notes = Storage.shared().noteList.filter({ $0.content.string.contains("- [ ] ") }) default: break } for note in notes { allTags.append(contentsOf: note.tags) } } let currentTags = sidebar.items[2].compactMap({ $0.name }) var toRemovePaths = [IndexPath]() var toRemoveTags = [String]() for tag in tags { if !allTags.contains(tag) { if let row = currentTags.firstIndex(of: tag) { toRemovePaths.append(IndexPath(row: row, section: 2)) toRemoveTags.append(tag) } } } sidebar.items[2].removeAll(where: { toRemoveTags.contains($0.name) }) deleteRows(at: toRemovePaths, with: .fade) deSelectTagIfNonExist(tags: toRemoveTags) } private func createQueryWithoutTags() -> SearchQuery? { guard let vc = viewController else { return nil } let query = SearchQuery() query.projects = vc.storage.searchQuery.projects if let type = vc.storage.searchQuery.type { query.type = type if query.projects.first != nil && type == .Tag { query.type = .Project } } return query } private func deSelectTagIfNonExist(tags: [String]) { guard let vc = viewController, let tag = vc.storage.searchQuery.tags.first else { return } guard tags.contains(tag) else { return } if let project = vc.storage.searchQuery.projects.first, let index = getIndexPathBy(project: project) { tableView(self, didSelectRowAt: index) return } if let type = vc.storage.searchQuery.type, let index = getIndexPathBy(type: type) { tableView(self, didSelectRowAt: index) } } public func getSelectedSidebarItem() -> SidebarItem? { guard let vc = viewController, let project = vc.storage.searchQuery.projects.first else { return nil } let items = sidebar.items for item in items { for subItem in item { if subItem.project == project { return subItem } } } return nil } public func getIndexPathBy(project: Project) -> IndexPath? { for (sectionId, section) in sidebar.items.enumerated() { for (rowId, item) in section.enumerated() { if item.project === project { let indexPath = IndexPath(row: rowId, section: sectionId) return indexPath } } } return nil } public func getIndexPathBy(tag: String) -> IndexPath? { let tagsSection = SidebarSection.Tags.rawValue for (rowId, item) in sidebar.items[tagsSection].enumerated() { if item.name == tag { let indexPath = IndexPath(row: rowId, section: tagsSection) return indexPath } } return nil } public func getIndexPathBy(type: SidebarItemType) -> IndexPath? { let section = SidebarSection.System.rawValue for (rowId, item) in sidebar.items[section].enumerated() { if item.type == type { let indexPath = IndexPath(row: rowId, section: section) return indexPath } } return nil } public func insertRows(projects: [Project]) { guard sidebar.items.indices.contains(1) else { return } var localItems = sidebar.items[1] let existingProjects = localItems.compactMap { $0.project } let toInsert = projects .filter { !existingProjects.contains($0) && $0.settings.showInSidebar } .sorted { $0.label.localizedCaseInsensitiveCompare($1.label) == .orderedAscending } guard !toInsert.isEmpty else { return } performBatchUpdates({ for project in toInsert { let insertIndex = localItems.firstIndex { $0.name.localizedCaseInsensitiveCompare(project.label) == .orderedDescending } ?? localItems.count let item = SidebarItem( name: project.label, project: project, type: .Project ) localItems.insert(item, at: insertIndex) sidebar.items[1].insert(item, at: insertIndex) insertRows(at: [IndexPath(row: insertIndex, section: 1)], with: .fade) } }, completion: { _ in UIApplication.getVC().resizeSidebar() }) } public func removeRows(projects: [Project]) { guard let vc = viewController else { return } let toDelete: [Project] = projects.flatMap { [$0] + $0.getChildProjectsByURL() } guard !toDelete.isEmpty else { return } var deselectCurrent = false performBatchUpdates({ var indexPathsToDelete = [IndexPath]() for index in sidebar.items[1].indices.reversed() { let item = sidebar.items[1][index] guard let project = item.project else { continue } if toDelete.contains(project) { sidebar.items[1].remove(at: index) indexPathsToDelete.append(IndexPath(row: index, section: 1)) if project == vc.storage.searchQuery.projects.first { deselectCurrent = true } vc.storage.remove(project: project) } } if !indexPathsToDelete.isEmpty { deleteRows(at: indexPathsToDelete, with: .automatic) } }, completion: { _ in if deselectCurrent { vc.notesTable.notes.removeAll() vc.notesTable.reloadData() let indexPath = IndexPath(row: 0, section: 0) self.tableView(self, didSelectRowAt: indexPath) } UIApplication.getVC().resizeSidebar() }) } public func select(project: Project) { guard let indexPath = getIndexPathBy(project: project) else { return } tableView(self, didSelectRowAt: indexPath) } public func select(tag: String) { guard let indexPath = getIndexPathBy(tag: tag) else { return } tableView(self, didSelectRowAt: indexPath) } public func remove(tag: String) { guard let indexPath = getIndexPathBy(tag: tag) else { return } sidebar.items[2].removeAll(where: { $0.name == tag}) deleteRows(at: [indexPath], with: .automatic) selectCurrentProject() } public func reloadSidebar() { sidebar = Sidebar() reloadData() var indexPath = IndexPath(row: 0, section: 0) if let projectURL = UserDefaultsManagement.lastProjectURL, let project = Storage.shared().getProjectBy(url: projectURL), let path = getIndexPathBy(project: project) { indexPath = path } else if let rowId = UserDefaultsManagement.lastSidebarItem { indexPath = IndexPath(row: rowId, section: 0) } tableView(self, didSelectRowAt: indexPath) viewController?.resizeSidebar(withAnimation: true) } public func reload(indexPath: IndexPath) { // Important as cell resets after reloadRows let currentPath = indexPathForSelectedRow reloadRows(at: [indexPath], with: .automatic) selectRow(at: currentPath, animated: false, scrollPosition: .none) } } ================================================ FILE: FSNotes iOS/ViewController+More.swift ================================================ // // ViewController+More.swift // FSNotes iOS // // Created by Олександр Глущенко on 10.01.2021. // Copyright © 2021 Oleksandr Glushchenko. All rights reserved. // import Foundation import UIKit extension ViewController: UIDocumentPickerDelegate { func makeSidebarSettingsMenu(for sidebarItem: SidebarItem) -> UIMenu? { let project = sidebarItem.project let handler: (_ action: UIAction) -> () = { action in switch action.identifier.rawValue { case "emptyBin": self.emptyBin() case "importNote": self.importNote(selectedProject: project) case "viewSettings": self.openProjectSettings(sidebarItem: sidebarItem) case "gitSettings": self.openGitSettings(selectedProject: project) case "bulkEditing": self.bulkEditing() case "createFolder": self.createFolder(selectedProject: project) case "removeFolder": self.removeFolder(selectedProject: project) case "renameFolder": self.renameFolder(selectedProject: project) case "removeTag": self.removeTag(sidebarItem: sidebarItem) case "renameTag": self.renameTag(sidebarItem: sidebarItem) case "openInFiles": self.openInFiles(selectedProject: project) case "lockFolder": self.lockProject(selectedProject: project) case "unlockFolder": self.unlockProject(selectedProject: project) case "decryptFolder": self.decryptProject(selectedProject: project) case "encryptFolder": self.encryptProject(selectedProject: project) case "gitAddCommitPush": self.addCommitPush(selectedProject: project) default: break } } // Build popovers var popoverActions = [FolderPopoverActions]() switch sidebarItem.type { case .Inbox: popoverActions = [.importNote, .settingsFolder, .createFolder, .multipleSelection, .openInFiles, .settingsRepository] case .All, .Todo: popoverActions = [.settingsFolder, .createFolder, .multipleSelection] case .Trash: popoverActions = [.settingsFolder, .multipleSelection, .openInFiles, .emptyBin] case .Project: popoverActions = [.importNote, .settingsFolder, .createFolder, .removeFolder, .renameFolder, .multipleSelection, .openInFiles, .settingsRepository, .encryptFolder] case .Tag: popoverActions = [.removeTag, .renameTag, .multipleSelection] case .Untagged: popoverActions = [.createFolder, .multipleSelection] case .ProjectEncryptedLocked: popoverActions = [.unLockFolder, .decryptFolder, .settingsFolder, .removeFolder, .renameFolder, .multipleSelection, .openInFiles, .settingsRepository] case .ProjectEncryptedUnlocked: popoverActions = [.lockFolder, .decryptFolder, .importNote, .settingsFolder, .createFolder, .removeFolder, .renameFolder, .multipleSelection, .openInFiles, .settingsRepository] default: break } // Build actions var actions = [UIAction]() if popoverActions.contains(.removeFolder) { let title = NSLocalizedString("Remove Folder", comment: "Main view popover table") actions.append(UIAction(title: title, image: UIImage(systemName: "trash"), identifier: UIAction.Identifier("removeFolder"), attributes: .destructive, handler: handler)) } if popoverActions.contains(.emptyBin) { let title = NSLocalizedString("Empty Bin", comment: "Main view popover table") actions.append(UIAction(title: title, image: UIImage(systemName: "xmark.circle"), identifier: UIAction.Identifier("emptyBin"), handler: handler)) } if popoverActions.contains(.importNote) { let title = NSLocalizedString("Import Notes", comment: "Main view popover table") actions.append(UIAction(title: title, image: UIImage(systemName: "square.and.arrow.down"), identifier: UIAction.Identifier("importNote"), handler: handler)) } if popoverActions.contains(.settingsFolder) { let title = NSLocalizedString("View Settings", comment: "Main view popover table") actions.append(UIAction(title: title, image: UIImage(systemName: "gearshape"), identifier: UIAction.Identifier("viewSettings"), handler: handler)) } if popoverActions.contains(.settingsRepository) { let title = NSLocalizedString("Git Settings", comment: "Main view popover table") actions.append(UIAction(title: title, image: UIImage(named: "gitSettings"), identifier: UIAction.Identifier("gitSettings"), handler: handler)) if let project = sidebarItem.project, project.getGitProject() != nil { let titleAddCommit = NSLocalizedString("Git Add/commit/push", comment: "Main view popover table") actions.append(UIAction(title: titleAddCommit, image: UIImage(systemName: "plus.circle"), identifier: UIAction.Identifier("gitAddCommitPush"), handler: handler)) } } if popoverActions.contains(.multipleSelection) { let title = NSLocalizedString("Select", comment: "Main view popover table") actions.append(UIAction(title: title, image: UIImage(systemName: "checkmark.circle"), identifier: UIAction.Identifier("bulkEditing"), handler: handler)) } if popoverActions.contains(.createFolder) { let title = NSLocalizedString("Create Folder", comment: "Main view popover table") actions.append(UIAction(title: title, image: UIImage(systemName: "folder.badge.plus"), identifier: UIAction.Identifier("createFolder"), handler: handler)) } if popoverActions.contains(.renameFolder) { let title = NSLocalizedString("Rename Folder", comment: "Main view popover table") actions.append(UIAction(title: title, image: UIImage(systemName: "pencil.circle"), identifier: UIAction.Identifier("renameFolder"), handler: handler)) } if popoverActions.contains(.removeTag) { let title = NSLocalizedString("Remove Tag", comment: "Main view popover table") actions.append(UIAction(title: title, image: UIImage(systemName: "tag.slash"), identifier: UIAction.Identifier("removeTag"), handler: handler)) } if popoverActions.contains(.renameTag) { let title = NSLocalizedString("Rename Tag", comment: "Main view popover table") actions.append(UIAction(title: title, image: UIImage(systemName: "pencil.circle"), identifier: UIAction.Identifier("renameTag"), handler: handler)) } if popoverActions.contains(.openInFiles) { let title = NSLocalizedString("Open in Files.app", comment: "Main view popover table") actions.append(UIAction(title: title, image: UIImage(systemName: "folder"), identifier: UIAction.Identifier("openInFiles"), handler: handler)) } if popoverActions.contains(.lockFolder) { let title = FolderPopoverActions.lockFolder.getDescription() actions.append(UIAction(title: title, image: UIImage(systemName: "lock"), identifier: UIAction.Identifier("lockFolder"), handler: handler)) } if popoverActions.contains(.unLockFolder) { let title = FolderPopoverActions.unLockFolder.getDescription() actions.append(UIAction(title: title, image: UIImage(systemName: "lock.open"), identifier: UIAction.Identifier("unlockFolder"), handler: handler)) } if popoverActions.contains(.decryptFolder) { let title = FolderPopoverActions.decryptFolder.getDescription() actions.append(UIAction(title: title, image: UIImage(systemName: "lock.slash"), identifier: UIAction.Identifier("decryptFolder"), handler: handler)) } if popoverActions.contains(.encryptFolder) { let title = FolderPopoverActions.encryptFolder.getDescription() actions.append(UIAction(title: title, image: UIImage(systemName: "lock"), identifier: UIAction.Identifier("encryptFolder"), handler: handler)) } // Build title var mainTitle = String() switch sidebarItem.type { case .Project: if let project = sidebarItem.project { mainTitle = project.getFullLabel() } case .Untagged: mainTitle = NSLocalizedString("Untagged", comment: "") default: mainTitle = sidebarItem.getName() } return UIMenu(title: mainTitle, children: actions) } @IBAction public func openSidebarSettings() { let mvc = UIApplication.getVC() if notesTable.isEditing { if let selectedRows = mvc.notesTable.selectedIndexPaths { var notes = [Note]() for indexPath in selectedRows { if mvc.notesTable.notes.indices.contains(indexPath.row) { let note = mvc.notesTable.notes[indexPath.row] notes.append(note) } } mvc.notesTable.selectedIndexPaths = nil mvc.notesTable.actionsSheet(notes: notes, presentController: self) } else { mvc.notesTable.allowsMultipleSelectionDuringEditing = false mvc.notesTable.setEditing(false, animated: true) } return } let sidebarItem = sidebarTableView.getSidebarItem() let projectLabel = sidebarItem?.project?.getFullLabel() ?? String() var type = sidebarItem?.type var indexPath: IndexPath? if let tag = Storage.shared().searchQuery.tags.first { indexPath = sidebarTableView.getIndexPathBy(tag: tag) } if let path = indexPath, path.section == SidebarSection.Tags.rawValue { type = .Tag } guard type != .Label else { return } var actions = [FolderPopoverActions]() switch type { case .Inbox: actions = [.importNote, .settingsFolder, .createFolder, .multipleSelection, .openInFiles, .settingsRepository] case .All, .Todo: actions = [.settingsFolder, .multipleSelection] case .Trash: actions = [.settingsFolder, .multipleSelection, .openInFiles, .emptyBin] case .Project: actions = [.importNote, .settingsFolder, .createFolder, .removeFolder, .renameFolder, .multipleSelection, .openInFiles, .settingsRepository, .encryptFolder] case .Tag: actions = [.removeTag, .renameTag, .multipleSelection] case .Untagged: actions = [.multipleSelection] case .ProjectEncryptedLocked: actions = [.unLockFolder, .decryptFolder, .importNote, .settingsFolder, .createFolder, .removeFolder, .renameFolder, .multipleSelection, .openInFiles, .settingsRepository] case .ProjectEncryptedUnlocked: actions = [.lockFolder, .decryptFolder, .importNote, .settingsFolder, .createFolder, .removeFolder, .renameFolder, .multipleSelection, .openInFiles, .settingsRepository] default: break } var mainTitle = type != .Tag && type == .Project ? projectLabel : sidebarItem?.getName() if type == .Untagged { mainTitle = NSLocalizedString("Untagged", comment: "") } let actionSheet = UIAlertController(title: mainTitle, message: nil, preferredStyle: .actionSheet) if actions.contains(.removeFolder) { let title = NSLocalizedString("Remove Folder", comment: "Main view popover table") let alertAction = UIAlertAction(title:title, style: .destructive, handler: { _ in self.removeFolder(selectedProject: sidebarItem?.project) }) alertAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "trash")?.resize(maxWidthHeight: 23) { alertAction.setValue(image, forKey: "image") } actionSheet.addAction(alertAction) } if actions.contains(.emptyBin) { let title = NSLocalizedString("Empty Bin", comment: "Main view popover table") let alertAction = UIAlertAction(title:title, style: .destructive, handler: { _ in self.emptyBin() }) alertAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "xmark.circle")?.resize(maxWidthHeight: 23) { alertAction.setValue(image, forKey: "image") } actionSheet.addAction(alertAction) } if actions.contains(.importNote) { let title = NSLocalizedString("Import Notes", comment: "Main view popover table") let importNote = UIAlertAction(title:title, style: .default, handler: { _ in self.importNote(selectedProject: sidebarItem?.project) }) importNote.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "square.and.arrow.down")?.resize(maxWidthHeight: 23) { importNote.setValue(image, forKey: "image") } actionSheet.addAction(importNote) } if actions.contains(.settingsFolder) { let title = NSLocalizedString("View Settings", comment: "Main view popover table") let settings = UIAlertAction(title:title, style: .default, handler: { _ in self.openProjectSettings(sidebarItem: sidebarItem) }) settings.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "gearshape")?.resize(maxWidthHeight: 23) { settings.setValue(image, forKey: "image") } actionSheet.addAction(settings) } if actions.contains(.settingsRepository) { let title = NSLocalizedString("Git Settings", comment: "Main view popover table") let alertAction = UIAlertAction(title:title, style: .default, handler: { _ in self.openGitSettings(selectedProject: sidebarItem?.project) }) alertAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(named: "gitSettings")?.resize(maxWidthHeight: 23) { alertAction.setValue(image, forKey: "image") } actionSheet.addAction(alertAction) } if actions.contains(.multipleSelection) { let title = NSLocalizedString("Select", comment: "Main view popover table") let multipleSelection = UIAlertAction(title:title, style: .default, handler: { _ in self.bulkEditing() }) multipleSelection.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "checkmark.circle")?.resize(maxWidthHeight: 23) { multipleSelection.setValue(image, forKey: "image") } actionSheet.addAction(multipleSelection) } if actions.contains(.createFolder) { let title = NSLocalizedString("Create Folder", comment: "Main view popover table") let alertAction = UIAlertAction(title:title, style: .default, handler: { _ in self.createFolder(selectedProject: sidebarItem?.project) }) alertAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "folder.badge.plus")?.resize(maxWidthHeight: 23) { alertAction.setValue(image, forKey: "image") } actionSheet.addAction(alertAction) } if actions.contains(.renameFolder) { let title = NSLocalizedString("Rename Folder", comment: "Main view popover table") let alertAction = UIAlertAction(title:title, style: .default, handler: { _ in self.renameFolder(selectedProject: sidebarItem?.project) }) alertAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "pencil.circle")?.resize(maxWidthHeight: 23) { alertAction.setValue(image, forKey: "image") } actionSheet.addAction(alertAction) } if actions.contains(.removeTag) { let title = NSLocalizedString("Remove Tag", comment: "Main view popover table") let alertAction = UIAlertAction(title:title, style: .destructive, handler: { _ in self.removeTag(sidebarItem: sidebarItem) }) alertAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "tag.slash")?.resize(maxWidthHeight: 23) { alertAction.setValue(image, forKey: "image") } actionSheet.addAction(alertAction) } if actions.contains(.renameTag) { let title = NSLocalizedString("Rename Tag", comment: "Main view popover table") let alertAction = UIAlertAction(title:title, style: .default, handler: { _ in self.renameTag(sidebarItem: sidebarItem) }) alertAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "pencil.circle")?.resize(maxWidthHeight: 23) { alertAction.setValue(image, forKey: "image") } actionSheet.addAction(alertAction) } if actions.contains(.openInFiles) { let title = NSLocalizedString("Open in Files.app", comment: "Main view popover table") let alertAction = UIAlertAction(title:title, style: .default, handler: { _ in self.openInFiles(selectedProject: sidebarItem?.project) }) alertAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "folder")?.resize(maxWidthHeight: 23) { alertAction.setValue(image, forKey: "image") } actionSheet.addAction(alertAction) } if actions.contains(.lockFolder) { let title = FolderPopoverActions.lockFolder.getDescription() let alertAction = UIAlertAction(title:title, style: .default, handler: { _ in self.lockProject(selectedProject: sidebarItem?.project) }) alertAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "lock")?.resize(maxWidthHeight: 23) { alertAction.setValue(image, forKey: "image") } actionSheet.addAction(alertAction) } if actions.contains(.unLockFolder) { let title = FolderPopoverActions.unLockFolder.getDescription() let alertAction = UIAlertAction(title:title, style: .default, handler: { _ in self.unlockProject(selectedProject: sidebarItem?.project) }) alertAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "lock.open")?.resize(maxWidthHeight: 23) { alertAction.setValue(image, forKey: "image") } actionSheet.addAction(alertAction) } if actions.contains(.decryptFolder) { let title = FolderPopoverActions.decryptFolder.getDescription() let alertAction = UIAlertAction(title:title, style: .default, handler: { _ in self.decryptProject(selectedProject: sidebarItem?.project) }) alertAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "lock.slash")?.resize(maxWidthHeight: 23) { alertAction.setValue(image, forKey: "image") } actionSheet.addAction(alertAction) } if actions.contains(.encryptFolder) { let title = FolderPopoverActions.encryptFolder.getDescription() let alertAction = UIAlertAction(title:title, style: .default, handler: { _ in self.encryptProject(selectedProject: sidebarItem?.project) }) alertAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment") if let image = UIImage(systemName: "lock")?.resize(maxWidthHeight: 23) { alertAction.setValue(image, forKey: "image") } actionSheet.addAction(alertAction) } let dismiss = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil) actionSheet.addAction(dismiss) actionSheet.popoverPresentationController?.sourceView = view actionSheet.popoverPresentationController?.sourceRect = CGRect(x: view.bounds.size.width / 2.0, y: view.bounds.size.height, width: 2.0, height: 1.0) present(actionSheet, animated: true, completion: nil) } func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { guard let projectURL = selectedProject?.url else { return } for url in urls { let dstURL = projectURL.appendingPathComponent(url.lastPathComponent) try? FileManager.default.copyItem(at: url, to: dstURL) } self.dismiss(animated: true, completion: nil) } private func importNote(selectedProject: Project?) { self.selectedProject = selectedProject let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.item], asCopy: true) picker.allowsMultipleSelection = true picker.delegate = self self.present(picker, animated: true, completion: nil) } public func openProjectSettings(sidebarItem: SidebarItem?) { let vc = UIApplication.getVC() let storage = Storage.shared() // All projects var currentProject = sidebarItem?.project if currentProject == nil { currentProject = storage.getCurrentProject() } // Virtual projects Notes and Todo if sidebarItem?.type == .Todo || sidebarItem?.type == .All { currentProject = sidebarItem?.project } guard let project = currentProject else { return } let projectController = ProjectSettingsViewController(project: project, dismiss: true) let controller = UINavigationController(rootViewController: projectController) self.dismiss(animated: true, completion: nil) vc.present(controller, animated: true, completion: nil) } @objc func bulkEditing() { let mvc = UIApplication.getVC() if !mvc.notesTable.isEditing { mvc.notesTable.allowsMultipleSelectionDuringEditing = true mvc.notesTable.setEditing(true, animated: true) // load navbar let cancelTitle = NSLocalizedString("Cancel", comment: "") navigationItem.rightBarButtonItem = UIBarButtonItem(title: cancelTitle, style: .plain, target: self, action: #selector(cancel)) // load toolbar let deleteImage = UIImage(systemName: "trash") let calendarImage = UIImage(systemName: "calendar") let duplicateImage = UIImage(systemName: "doc.on.doc") let moveImage = UIImage(systemName: "move.3d") if #available(iOS 14.0, *) { var items = [UIBarButtonItem]() items.append(UIBarButtonItem(image: deleteImage, style: .plain, target: self, action: #selector(removeNotes))) items.append(UIBarButtonItem.flexibleSpace()) items.append(UIBarButtonItem(image: calendarImage, style: .plain, target: self, action: #selector(calendarNotes))) items.append(UIBarButtonItem.flexibleSpace()) items.append(UIBarButtonItem(image: duplicateImage, style: .plain, target: self, action: #selector(duplicateNotes))) items.append(UIBarButtonItem.flexibleSpace()) items.append(UIBarButtonItem(image: moveImage, style: .plain, target: self, action: #selector(moveNotes))) toolbarItems = items } navigationController?.toolbar.tintColor = UIColor.mainTheme navigationController?.setToolbarHidden(false, animated: true) navigationController?.navigationBar.tintColor = UIColor.mainTheme } } public func configureSidebarNavMenu() { if let sidebarItem = UIApplication.getVC().lastSidebarItem { configureNavMenu(for: sidebarItem) } } @objc func removeNotes() { let notes = notesTable.getSelectedNotes() notesTable.removeAction(notes: notes) notesTable.turnOffEditing() configureSidebarNavMenu() navigationController?.setToolbarHidden(true, animated: true) configureToolbar() } @objc func calendarNotes() { let notes = notesTable.getSelectedNotes() notesTable.dateAction(notes: notes) notesTable.turnOffEditing() configureSidebarNavMenu() configureToolbar() navigationController?.setToolbarHidden(false, animated: true) } @objc func duplicateNotes() { let notes = notesTable.getSelectedNotes() notesTable.duplicateAction(notes: notes) notesTable.turnOffEditing() configureSidebarNavMenu() configureToolbar() navigationController?.setToolbarHidden(false, animated: true) } @objc func moveNotes() { let notes = notesTable.getSelectedNotes() notesTable.moveAction(notes: notes) notesTable.turnOffEditing() configureSidebarNavMenu() configureToolbar() navigationController?.setToolbarHidden(false, animated: true) } @objc func cancel() { notesTable.turnOffEditing() configureSidebarNavMenu() configureToolbar() navigationController?.setToolbarHidden(false, animated: true) } private func createFolder(selectedProject: Project?) { guard var selectedProject = selectedProject else { return } if selectedProject.isVirtual { selectedProject = self.storage.getDefault()! } let mvc = UIApplication.getVC() let alertController = UIAlertController(title: NSLocalizedString("Create folder:", comment: ""), message: nil, preferredStyle: .alert) alertController.addTextField(configurationHandler: { [] (textField: UITextField) in textField.placeholder = NSLocalizedString("Enter folder name", comment: "") }) let confirmAction = UIAlertAction(title: "OK", style: .default) { (_) in guard let name = alertController.textFields?[0].text, name.count > 0 else { return } let newDir = selectedProject.url.appendingPathComponent(name, isDirectory: true) do { try FileManager.default.createDirectory(at: newDir, withIntermediateDirectories: false, attributes: nil) } catch { print(error) return } if let projects = Storage.shared().insert(url: newDir) { OperationQueue.main.addOperation { UIApplication.getVC().sidebarTableView.insertRows(projects: projects) } } } let title = NSLocalizedString("Cancel", comment: "") let cancelAction = UIAlertAction(title: title, style: .cancel) { (_) in } alertController.addAction(confirmAction) alertController.addAction(cancelAction) self.dismiss(animated: true, completion: nil) mvc.present(alertController, animated: true) { alertController.textFields![0].selectAll(nil) } } private func removeFolder(selectedProject: Project?) { guard let selectedProject = selectedProject else { return } guard !selectedProject.isDefault else { return } let mvc = UIApplication.getVC() let alert = UIAlertController( title: "Folder removing 🚨", message: "Are you really want to remove \"\(selectedProject.label)\"? Folder content will be deleted, action can not be undone.", preferredStyle: UIAlertController.Style.alert ) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action: UIAlertAction!) in OperationQueue.main.addOperation { mvc.sidebarTableView.removeRows(projects: [selectedProject]) if !selectedProject.isBookmark { try? FileManager.default.removeItem(at: selectedProject.url) } Storage.shared().remove(project: selectedProject) } })) alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action: UIAlertAction!) in })) if selectedProject.isBookmark { OperationQueue.main.addOperation { let bookmark = SandboxBookmark.sharedInstance() bookmark.remove(url: selectedProject.url) mvc.sidebarTableView.removeRows(projects: [selectedProject]) } } else { mvc.present(alert, animated: true, completion: nil) } } private func renameFolder(selectedProject: Project?) { guard let selectedProject = selectedProject else { return } let mvc = UIApplication.getVC() let title = NSLocalizedString("Rename folder:", comment: "Popover table") let alertController = UIAlertController(title: title, message: nil, preferredStyle: .alert) alertController.addTextField(configurationHandler: { [] (textField: UITextField) in textField.placeholder = NSLocalizedString("Enter folder name", comment: "") textField.text = selectedProject.url.lastPathComponent }) let confirmAction = UIAlertAction(title: "OK", style: .default) { (_) in OperationQueue.main.addOperation { guard let name = alertController.textFields?[0].text, name.count > 0 else { return } let newDir = selectedProject.url .deletingLastPathComponent() .appendingPathComponent(name, isDirectory: true) do { try FileManager.default.moveItem(at: selectedProject.url, to: newDir) } catch { print(error) return } mvc.sidebarTableView.removeRows(projects: [selectedProject]) if let projects = self.storage.insert(url: newDir) { mvc.sidebarTableView.insertRows(projects: projects) if let first = projects.first { mvc.sidebarTableView.select(project: first) } } } } let cancel = NSLocalizedString("Cancel", comment: "") let cancelAction = UIAlertAction(title: cancel, style: .cancel) { (_) in } alertController.addAction(confirmAction) alertController.addAction(cancelAction) self.dismiss(animated: true, completion: nil) mvc.present(alertController, animated: true) { alertController.textFields![0].selectAll(nil) } } private func removeTag(sidebarItem: SidebarItem?) { let mvc = UIApplication.getVC() guard let sidebarItem = sidebarItem, sidebarItem.type == .Tag else { return } guard let selectedProject = mvc.storage.searchQuery.projects.first else { return } let tag = sidebarItem.name let notes = mvc.storage.noteList .filter({ $0.project == selectedProject }) .filter({ $0.tags.contains(tag) }) for note in notes { note.replace(tag: "#\(tag)", with: "") note.tags.removeAll(where: { $0 == tag }) } mvc.sidebarTableView.remove(tag: tag) self.dismiss(animated: true, completion: nil) } private func renameTag(sidebarItem: SidebarItem?) { let mvc = UIApplication.getVC() guard let sidebarItem = sidebarItem, sidebarItem.type == .Tag else { return } guard let selectedProject = mvc.storage.searchQuery.projects.first else { return } let tag = sidebarItem.name let title = NSLocalizedString("Rename tag:", comment: "Popover table") let alertController = UIAlertController(title: title, message: nil, preferredStyle: .alert) alertController.addTextField(configurationHandler: { [] (textField: UITextField) in textField.placeholder = NSLocalizedString("Enter new tag name", comment: "") textField.text = tag }) let confirmAction = UIAlertAction(title: "OK", style: .default) { (_) in guard var name = alertController.textFields?[0].text, name.count > 0 else { return } name = name.withoutSpecialCharacters guard name.count > 1 else { return } let notes = mvc.storage.noteList .filter({ $0.project == selectedProject }) .filter({ $0.tags.contains(tag) }) for note in notes { note.replace(tag: "#\(tag)", with: "#\(name)") note.tags.removeAll(where: { $0 == tag }) _ = note.scanContentTags() } mvc.sidebarTableView.remove(tag: tag) mvc.sidebarTableView.insert(tags: [name]) self.dismiss(animated: true, completion: nil) } let cancel = NSLocalizedString("Cancel", comment: "") let cancelAction = UIAlertAction(title: cancel, style: .cancel) { (_) in } alertController.addAction(confirmAction) alertController.addAction(cancelAction) self.dismiss(animated: true, completion: nil) mvc.present(alertController, animated: true) { alertController.textFields![0].selectAll(nil) } } private func openInFiles(selectedProject: Project?) { guard let selectedProject = selectedProject else { return } guard let path = selectedProject.url.path.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlPathAllowed) else { return } if let projectUrl = URL(string: "shareddocuments://" + path) { UIApplication.shared.open(projectUrl, options: [:]) } } private func openGitSettings(selectedProject: Project?) { guard let selectedProject = selectedProject else { return } let projectController = AppDelegate.getGitVC(for: selectedProject) let controller = UINavigationController(rootViewController: projectController) self.dismiss(animated: true, completion: nil) UIApplication.getVC().present(controller, animated: true, completion: nil) } private func emptyBin() { let notes = storage.getAllTrash() storage.removeNotes(notes: notes, fsRemove: true, completely: true) { [self]_ in self.notesTable.removeRows(notes: notes) } } @objc public func unlock() { guard let project = sidebarTableView.getSelectedSidebarItem()?.project else { return } unlockProject(selectedProject: project) } public func unlockProject(selectedProject: Project?, createNote: Bool = false) { guard let selectedProject = selectedProject else { return } getMasterPassword() { password in let result = selectedProject.unlock(password: password) DispatchQueue.main.async { guard result.1.count > 0 || result.0.count == 0 else { self.wrongPassAlert() return } self.sidebarTableView.loadTags(notes: result.1) self.disableLockedProject() if let indexPath = self.sidebarTableView.getIndexPathBy(project: selectedProject), let sidebarItem = self.sidebarTableView.getSidebarItem(project: selectedProject) { sidebarItem.load(type: .ProjectEncryptedUnlocked) self.sidebarTableView.reload(indexPath: indexPath) self.sidebarTableView.select(project: selectedProject) if createNote { self.createNote() } } self.reloadNotesTable() self.configureSidebarNavMenu() } } } public func lockProject(selectedProject: Project?) { guard let selectedProject = selectedProject else { return } let locked = selectedProject.lock() selectedProject.removeCache() DispatchQueue.main.async { self.sidebarTableView.loadTags(notes: locked) self.enableLockedProject() if let indexPath = self.sidebarTableView.getIndexPathBy(project: selectedProject), let sidebarItem = self.sidebarTableView.getSidebarItem(project: selectedProject) { sidebarItem.load(type: .ProjectEncryptedLocked) self.sidebarTableView.reload(indexPath: indexPath) self.sidebarTableView.select(project: selectedProject) } self.reloadNotesTable() self.configureSidebarNavMenu() } } public func encryptProject(selectedProject: Project?) { guard let selectedProject = selectedProject else { return } getMasterPassword() { password in let encrypted = selectedProject.encrypt(password: password) selectedProject.removeCache() DispatchQueue.main.async { self.sidebarTableView.loadTags(notes: encrypted) self.enableLockedProject() if let indexPath = self.sidebarTableView.getIndexPathBy(project: selectedProject), let sidebarItem = self.sidebarTableView.getSidebarItem(project: selectedProject) { sidebarItem.load(type: .ProjectEncryptedLocked) self.sidebarTableView.reload(indexPath: indexPath) self.sidebarTableView.select(project: selectedProject) } self.reloadNotesTable() self.configureSidebarNavMenu() } } } public func addCommitPush(selectedProject: Project?) { guard let selectedProject = selectedProject?.getGitProject() else { return } notesTable.saveRevisionAction(project: selectedProject) } public func decryptProject(selectedProject: Project?) { guard let selectedProject = selectedProject else { return } getMasterPassword() { password in let notes = selectedProject.storage.getNotesBy(project: selectedProject) let decrypted = selectedProject.decrypt(password: password) DispatchQueue.main.async { guard decrypted.count > 0 || notes.count == 0 else { self.wrongPassAlert() return } self.sidebarTableView.loadTags(notes: decrypted) self.disableLockedProject() if let indexPath = self.sidebarTableView.getIndexPathBy(project: selectedProject), let sidebarItem = self.sidebarTableView.getSidebarItem(project: selectedProject) { sidebarItem.load(type: .Project) self.sidebarTableView.reload(indexPath: indexPath) self.sidebarTableView.select(project: selectedProject) } self.reloadNotesTable() self.configureSidebarNavMenu() } } } private func wrongPassAlert() { let message = NSLocalizedString("Wrong password", comment: "") let alertController = UIAlertController(title: message, message: nil, preferredStyle: .alert) let okAction = UIAlertAction(title: "OK", style: .cancel) { (_) in } alertController.addAction(okAction) self.present(alertController, animated: true, completion: nil) } } ================================================ FILE: FSNotes iOS/ViewController.swift ================================================ // // ViewController.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 1/29/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit import LocalAuthentication import WebKit import AudioToolbox import CoreSpotlight class ViewController: UIViewController, UISearchBarDelegate, UIGestureRecognizerDelegate, UISearchControllerDelegate { @IBOutlet weak var sidebarTableBottomConstraint: NSLayoutConstraint! @IBOutlet weak var notesTableBottomContraint: NSLayoutConstraint! @IBOutlet weak var notesTableLeadingConstraint: NSLayoutConstraint! @IBOutlet weak var sidebarTableLeadingConstraint: NSLayoutConstraint! @IBOutlet weak var sidebarTableWidth: NSLayoutConstraint! @IBOutlet weak var notesTable: NotesTableView! @IBOutlet weak var sidebarTableView: SidebarTableView! @IBOutlet weak var leftPreSafeArea: UIView! @IBOutlet weak var rightPreSafeArea: UIView! @IBOutlet weak var lockedProject: UIImageView! private var newsPopup: MPreviewView? private var newsOverlay: UIView? public var indicator: UIActivityIndicatorView? public var storage = Storage.shared() public var cloudDriveManager: CloudDriveManager? private let searchQueue = OperationQueue() private let metadataQueue = OperationQueue() public let gitQueue = OperationQueue() public let gitQueueState = OperationQueue() private var delayedInsert: Note? private var maxSidebarWidth = CGFloat(0) private var accessTime = DispatchTime.now() public var isActiveTableUpdating = false private var queryDidFinishGatheringObserver : Any? private var isBackground: Bool = false public var shouldReturnToControllerIndex = false // Swipe animation from handleSidebarSwipe private var sidebarWidth: CGFloat = 0 private var isLandscape: Bool? public var restoreFindID: String? public var isLoadedDB: Bool = false public var isLoadedSidebar: Bool = false public var folderCapacity: String? public var currentFolder: String? lazy var searchBar = UISearchBar(frame: CGRect.zero) // Pass for access from CloudDriveManager public var editorViewController: EditorViewController? private var gitClean: Bool = false private var gitPullTimer: Timer? private var searchFocus: Bool = false private var searchString: String? = nil // Project for import picker public var selectedProject: Project? public var initialLoadingState = false override func viewWillAppear(_ animated: Bool) { navigationController?.navigationBar.prefersLargeTitles = false super.viewWillAppear(animated) navigationItem.searchController = nil } override func viewDidAppear(_ animated: Bool) { if nil == Storage.shared().getRoot() { let alert = UIAlertController(title: "Storage not found", message: "Please enable iCloud Drive for this app and try again!", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .destructive, handler: { action in exit(0) })) self.present(alert, animated: true, completion: nil) } // Clean preview after previous loading UIApplication.getEVC().getPreviewView()?.clean() // If return from editor UIApplication.getEVC().userActivity?.invalidate() loadPreSafeArea() if let sidebarItem = UIApplication.getVC().lastSidebarItem { configureNavMenu(for: sidebarItem) } if searchFocus { disableSearchFocus() DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: { if let text = self.searchString { self.navigationItem.searchController?.searchBar.text = text } self.navigationItem.searchController?.searchBar.becomeFirstResponder() }) } else if !isLoadedSidebar { notesTable.showLoader() } super.viewDidAppear(animated) configureSearchController() } override func viewDidLoad() { startCloudDriveSyncEngine() configureUI() configureNotifications() configureGestures() gitQueue.qualityOfService = .userInteractive gitQueue.maxConcurrentOperationCount = 1 gitQueue.isSuspended = true gitQueueState.qualityOfService = .background gitQueueState.maxConcurrentOperationCount = 1 scheduledGitPull() disableLockedProject() loadSidebar() loadNotches() loadPreSafeArea() if !initialLoadingState { configureSearchController() initialLoadingState = true loadNews() if let sceneDelegate = UIApplication.getSceneDelegate(), let shortcut = sceneDelegate.launchedShortcutItem { handleShortCutItem(shortcut) sceneDelegate.launchedShortcutItem = nil } else { self.restoreLastController() } DispatchQueue.global(qos: .userInteractive).async { self.loadDB() } } super.viewDidLoad() configureToolbar() isLandscape = UIDevice.current.orientation.isLandscape } @objc public func didBecomeActive() { DispatchQueue.global(qos: .background).async { self.checkExternal() } addPullTask() } public func scheduledGitPull() { // Scheduling timer to Call the function "updateCounting" with the interval of 1 seconds gitPullTimer?.invalidate() gitPullTimer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(self.addPullTask), userInfo: nil, repeats: true) } public func startCloudDriveSyncEngine(completion: (() -> ())? = nil) { guard UserDefaultsManagement.iCloudDrive else { return } cloudDriveManager = CloudDriveManager(delegate: self, storage: self.storage) cloudDriveManager?.metadataQuery.disableUpdates() if let cdm = self.cloudDriveManager { self.queryDidFinishGatheringObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: cdm.metadataQuery, queue: self.metadataQueue) { notification in cdm.queryDidFinishGathering(notification: (notification as NSNotification)) completion?() NotificationCenter.default.removeObserver(self.queryDidFinishGatheringObserver as Any, name: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: nil) NotificationCenter.default.addObserver(forName: NSNotification.Name.NSMetadataQueryDidUpdate, object: cdm.metadataQuery, queue: self.metadataQueue) { notification in UIApplication.shared.runInBackground({ cdm.handleMetadataQueryUpdates(notification: notification as NSNotification) }) } } self.cloudDriveManager?.metadataQuery.start() } } public func stopCloudDriveSyncEngine() { self.cloudDriveManager?.metadataQuery.stop() } public func configureUI() { //UINavigationBar.appearance().isTranslucent = true self.metadataQueue.qualityOfService = .userInteractive self.indicator = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.large) navigationItem.leftBarButtonItems = [ UIBarButtonItem(systemImageName: "sidebar.left", target: self, selector: #selector(openSidebar)), UIBarButtonItem(systemImageName: "gear", target: self, selector: #selector(openSettings)) ] setNavTitle(folder: NSLocalizedString("Inbox", comment: "")) sidebarTableView.backgroundColor = UIColor.sidebar sidebarTableView.dropDelegate = sidebarTableView if #available(iOS 15.0, *) { sidebarTableView.sectionHeaderTopPadding = 0 } notesTable.viewDelegate = self notesTable.dragInteractionEnabled = true notesTable.dragDelegate = notesTable notesTable.keyboardDismissMode = .interactive notesTable.contentInsetAdjustmentBehavior = .never notesTable.alwaysBounceVertical = true notesTable.dataSource = notesTable notesTable.delegate = notesTable notesTable.layer.zPosition = 100 notesTable.rowHeight = UITableView.automaticDimension notesTable.estimatedRowHeight = 160 notesTable.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: CGFloat.leastNormalMagnitude)) } public var lastSidebarItem: SidebarItem? = nil public func configureNavMenu(for sidebarItem: SidebarItem) { lastSidebarItem = sidebarItem if let menu = makeSidebarSettingsMenu(for: sidebarItem) { navigationItem.rightBarButtonItem = UIBarButtonItem(systemImageName: "ellipsis.circle", menu: menu) } } public func setNavTitle(folder: String? = nil, qty: String? = nil) { if let folder = folder { currentFolder = folder } if let qty = qty { folderCapacity = qty } let folder = currentFolder ?? "" var qty = folderCapacity ?? "∞" if let item = sidebarTableView.getSidebarItem()?.project, item.isCleanGit { qty += " | git ✓" } navigationItem.title = folder if #available(iOS 26.0, *) { navigationItem.subtitle = qty } } public func configureNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(ubiquitousKeyValueStoreDidChange(_:)), name: NSUbiquitousKeyValueStore.didChangeExternallyNotification, object: NSUbiquitousKeyValueStore.default) if NSUbiquitousKeyValueStore.default.synchronize() == false { fatalError("This app was not built with the proper entitlement requests.") } NSUbiquitousKeyValueStore.default.synchronize() NotificationCenter.default.addObserver(self, selector: #selector(preferredContentSizeChanged), name: UIContentSizeCategory.didChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: UIDevice.orientationDidChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector:#selector(willExitForeground), name: UIApplication.willEnterForegroundNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil) } public func configureGestures() { let swipe = UIPanGestureRecognizer(target: self, action: #selector(handleSidebarSwipe)) swipe.minimumNumberOfTouches = 1 swipe.delegate = self view.addGestureRecognizer(swipe) let longTapOnSidebar = UILongPressGestureRecognizer(target: self, action: #selector(sidebarLongPress)) longTapOnSidebar.minimumPressDuration = 0.5 view.addGestureRecognizer(longTapOnSidebar) } public func configureSearchController() { let text = navigationItem.searchController?.searchBar.text let searchController = UISearchController(searchResultsController: nil) searchController.hidesNavigationBarDuringPresentation = false searchController.searchBar.delegate = self searchController.searchBar.searchBarStyle = .minimal searchController.searchBar.placeholder = NSLocalizedString("Search or create", comment: "") searchController.searchBar.returnKeyType = .done if #available(iOS 26.0, *) { searchController.searchBar.showsCancelButton = true } else { searchController.searchBar.showsCancelButton = false } searchController.searchBar.autocapitalizationType = .none searchController.searchBar.keyboardAppearance = traitCollection.userInterfaceStyle == .dark ? .dark : .default if let text = text { searchController.searchBar.text = text } navigationItem.searchController = searchController navigationController?.setToolbarHidden(false, animated: true) } public func configureToolbar() { var items = [UIBarButtonItem]() if #available(iOS 26.0, *) { items.append(navigationItem.searchBarPlacementBarButtonItem) } items.append(.flexibleSpace()) items.append( Buttons.getNewNote( target: self, selector: #selector(newButtonAction) ) ) if needsRightPadding() { let rightPadding = UIBarButtonItem( barButtonSystemItem: .fixedSpace, target: nil, action: nil ) rightPadding.width = 30 items.append(rightPadding) } toolbarItems = items } private func needsRightPadding() -> Bool { if #available(iOS 26.0, *) { return false } return true } public func enableSearchFocus(string: String? = nil) { searchFocus = true searchString = string } public func disableSearchFocus() { searchFocus = false } public func handleShortCutItem(_ shortcutItem: UIApplicationShortcutItem) { guard ShortcutIdentifier(fullType: shortcutItem.type) != nil else { return } guard let shortCutType = shortcutItem.type as String? else { return } switch shortCutType { case ShortcutIdentifier.makeNew.type: self.createNote() break case ShortcutIdentifier.clipboard.type: self.createNote(pasteboard: true) break case ShortcutIdentifier.search.type: self.loadViewIfNeeded() self.enableSearchFocus() self.popViewController() self.loadSearchController() break default: break } } @IBAction public func openSidebar() { if UserDefaultsManagement.sidebarIsOpened { hideSidebar() } else { showSidebar() } } @IBAction public func sidebarLongPress(gesture: UILongPressGestureRecognizer) { guard UserDefaultsManagement.sidebarIsOpened else { return } let p = gesture.location(in: self.sidebarTableView) guard p.x < maxSidebarWidth, let indexPath = self.sidebarTableView.indexPathForRow(at: p) else { return } if gesture.state != .ended { sidebarTableView.tableView(sidebarTableView, didSelectRowAt: indexPath) openSidebarSettings() } gesture.state = .ended } public func loadSidebar() { sidebarTableView.dataSource = self.sidebarTableView sidebarTableView.delegate = self.sidebarTableView sidebarTableView.viewController = self maxSidebarWidth = self.calculateLabelMaxWidth() lockedProject.layer.zPosition = 1001 lockedProject.isUserInteractionEnabled = true let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(unlock)) lockedProject.addGestureRecognizer(tapRecognizer) initSidebar() if UserDefaultsManagement.sidebarIsOpened { resizeSidebar() } } public func loadDB() { let storage = self.storage let dirsLoading = Date() storage.loadNonSystemProject() storage.loadProjectRelations() print("1. Loaded non system projects and relations in \(dirsLoading.timeIntervalSinceNow * -1) seconds") let notesLoadingPoint = Date() let projects = storage.getProjects() for project in projects { // print("Reading project: \(project.label) (\(project.url))") _ = project.loadNotes() } print("2. Notes loading finished in \(notesLoadingPoint.timeIntervalSinceNow * -1) seconds") OperationQueue.main.addOperation { self.importSavedInSharedExtension() self.sidebarTableView.reloadSidebar() DispatchQueue.global(qos: .userInitiated).async { let diffLoading = Date() for project in storage.getProjects() { let changes = project.checkNotesCacheDiff() self.notesTable.doVisualChanges(results: changes) } print("3. Notes diff loading finished in \(diffLoading.timeIntervalSinceNow * -1) seconds") // find:// if let restore = self.restoreFindID { self.restoreFindID = nil if let note = Storage.shared().getBy(title: restore) { OperationQueue.main.addOperation { self.notesTable.hideLoader() UIApplication.getEVC().load(note: note) } } } // Load notes content let notesFullLoading = Date() self.storage.loadNotesContent() print("4. Full notes loading in \(notesFullLoading.timeIntervalSinceNow * -1) seconds") let spotlightPoint = Date() self.reIndexSpotlight() print("5. Spotlight indexation finished in \(spotlightPoint.timeIntervalSinceNow * -1) seconds") // enable iCloud Drive updates after projects structure formalized self.cloudDriveManager?.metadataQuery.enableUpdates() self.isLoadedDB = true self.gitQueue.isSuspended = false } } } private func reIndexSpotlight() { CSSearchableIndex.default().deleteAllSearchableItems { (error) in if let error = error { print("Spotlight \(error)") } } var spotlightItems = [CSSearchableItem]() for note in storage.noteList { if note.project.isTrash || !note.project.settings.showInCommon { continue } let attributed = CSSearchableItemAttributeSet(itemContentType: "Text") attributed.title = note.title attributed.contentDescription = note.content.string attributed.lastUsedDate = note.modifiedLocalAt let item = CSSearchableItem(uniqueIdentifier: note.url.path, domainIdentifier: "Notes", attributeSet: attributed) spotlightItems.append(item) } CSSearchableIndex.default().indexSearchableItems(spotlightItems) { (error) in if let error = error { print("Spotlight \(error)") } } } public func updateSpotlightIndex(notes: [Note]) { var items = [CSSearchableItem]() for note in notes { let attributed = CSSearchableItemAttributeSet(itemContentType: "Text") attributed.title = note.title attributed.contentDescription = note.content.string attributed.lastUsedDate = note.modifiedLocalAt let item = CSSearchableItem(uniqueIdentifier: note.url.path, domainIdentifier: "Notes", attributeSet: attributed) items.append(item) } CSSearchableIndex.default().indexSearchableItems(items, completionHandler: nil) } public func removeSpotlightIndex(notes: [Note]) { var idents = [String]() for note in notes { idents.append(note.url.path) } CSSearchableIndex.default().deleteSearchableItems(withDomainIdentifiers: idents, completionHandler: nil) } private func loadNews() { guard storage.isReadedNewsOutdated(), let newsURL = storage.getNews(), let defaultProject = storage.getDefault() else { return } let isLandscape = UIDevice.current.orientation.isLandscape newsPopup?.removeFromSuperview() newsOverlay?.removeFromSuperview() let screeenWidth = UIScreen.main.bounds.width let screeenHeight = UIScreen.main.bounds.height let overlay = UIView(frame: CGRect(x: 0, y: 0, width: screeenWidth, height: screeenHeight)) overlay.layer.zPosition = 104 overlay.backgroundColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.5) UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.addSubview(overlay) self.newsOverlay = overlay var width = UIScreen.main.bounds.width - 20 if isLandscape { width = UIScreen.main.bounds.width * 0.75 } let height = screeenHeight * 0.75 let note = Note(url: newsURL, with: defaultProject) note.load() let frame = CGRect( x: (screeenWidth - width) / 2, y: (screeenHeight - height) / 2, width: width, height: height ) let news = MPreviewView(frame: frame, note: note, closure: {}) news.layer.zPosition = 105 news.backgroundColor = UIColor.white news.layer.cornerRadius = 5 news.layer.masksToBounds = true news.layer.borderWidth = 1 news.layer.borderColor = UIColor.gray.cgColor if #available(iOS 15.0, *) { var config = UIButton.Configuration.plain() config.image = UIImage(systemName: "xmark.circle.fill") config.preferredSymbolConfigurationForImage = .init(pointSize: 25) config.baseForegroundColor = UIColor.mainTheme let closeButton = UIButton(frame: CGRect(x: width - 5 - 60, y: 5, width: 60, height: 60)) closeButton.configuration = config closeButton.addTarget(self, action: #selector(closeNews), for: .touchDown) news.addSubview(closeButton) } UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.addSubview(news) self.newsPopup = news } func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if let recognizer = gestureRecognizer as? UIPanGestureRecognizer { if recognizer.translation(in: self.view).x > 0 && !UserDefaultsManagement.sidebarIsOpened || recognizer.translation(in: self.view).x < 0 && UserDefaultsManagement.sidebarIsOpened { return true } } return false } public func getLeftInset() -> CGFloat { return view.safeAreaInsets.left } public func getRightInset() -> CGFloat { return view.safeAreaInsets.right } public func loadNotches() { rightPreSafeArea.backgroundColor = .whiteBlack } public func loadPreSafeArea() { if UserDefaultsManagement.sidebarIsOpened { // blue/black pre safe area leftPreSafeArea.backgroundColor = UIColor.sidebar rightPreSafeArea.backgroundColor = .whiteBlack } else { leftPreSafeArea.backgroundColor = .whiteBlack rightPreSafeArea.backgroundColor = .whiteBlack } } @objc public func openSettings() { navigationController?.interactivePopGestureRecognizer?.delegate = nil navigationController?.pushViewController(SettingsViewController(), animated: true) } @objc func ubiquitousKeyValueStoreDidChange(_ notification: NSNotification) { if let keys = notification.userInfo?[NSUbiquitousKeyValueStoreChangedKeysKey] as? [String] { for key in keys { if key == "co.fluder.fsnotes.pins.shared" { let result = storage.restoreCloudPins() DispatchQueue.main.async { if let added = result.added { self.notesTable.addPins(notes: added) } if let removed = result.removed { self.notesTable.removePins(notes: removed) } } } if key.startsWith(string: "es.fsnot.project-settings") { let settingsKey = key.replacingOccurrences(of: "es.fsnot.project-settings", with: "") if let project = storage.getProjectBy(settingsKey: settingsKey) { project.reloadSettings() DispatchQueue.main.async { if let result = project.loadWebAPI() { self.notesTable.reloadRows(notes: result.0 + result.1) } } } } } } } @objc func toggleSearch(refreshControl: UIRefreshControl) { if storage.hasOrigins() { addPullTask(force: true) } else { toggleSearchView() } refreshControl.endRefreshing() } @objc func addPullTask(force: Bool = false) { guard storage.hasOrigins() else { return } guard UIApplication.getVC().gitQueue.operationCount == 0 else { print("Pull skipped") return } let viewController = UIApplication.getVC() viewController.gitQueue.addOperation({ Storage.shared().pullAll(force: force) if !UserDefaultsManagement.iCloudDrive { self.checkNew() } // if viewController.gitQueueState.operationCount == 0 { // viewController.gitQueueState.addOperation { // Storage.shared().checkGitState() // DispatchQueue.main.async { // self.updateNotesCounter() // } // } // } }) } public func checkNew() { if let projects = Storage.shared().getGitProjects() { for project in projects { if let childProjects = project.getAllChild() { for childProject in childProjects { let changes = childProject.checkNotesCacheDiff(isGit: true) self.notesTable.doVisualChanges(results: changes) } } let changes = project.checkNotesCacheDiff(isGit: true) self.notesTable.doVisualChanges(results: changes) } } } public func checkExternal() { let projects = Storage.shared().projects.filter({ $0.isBookmark }) guard projects.count > 0 else { return } var remove = [Note]() var insert = [Note]() var reload = [Note]() for project in projects { if let childProjects = project.getAllChild() { for childProject in childProjects { let changes = childProject.checkFSAndMemoryDiff() remove += changes.0 insert += changes.1 reload += changes.2 } } let changes = project.checkFSAndMemoryDiff() remove += changes.0 insert += changes.1 reload += changes.2 } for note in insert { note.loadPreviewInfo() } for note in reload { note.invalidateCache() note.loadPreviewInfo() } self.notesTable.doVisualChanges(results: (remove, insert, reload)) } public func loadSearchController(query: String? = nil) { if let query = query { navigationItem.searchController?.searchBar.text = query } DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.navigationItem.searchController?.searchBar.becomeFirstResponder() } } private func toggleSearchView() { loadSearchController() sidebarTableView.deselectAll() reloadNotesTable() } func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { buildSearchQuery() reloadNotesTable() } func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { if #available(iOS 26.0, *) { if let sc = navigationItem.searchController { sc.isActive = false sc.searchBar.resignFirstResponder() } } else { configureSearchController() } } func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { notesTable.setContentOffset(CGPoint(x: 0, y: -44), animated: true) disableLockedProject() } func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { let content = searchBar.text searchBar.text = "" buildSearchQuery() reloadNotesTable() self.createNote(content: content) } public func configureIndicator(indicator: UIActivityIndicatorView, view: UIView) { indicator.frame = CGRect(x: 0.0, y: 0.0, width: 50.0, height: 50.0) indicator.center = view.center indicator.layer.cornerRadius = 5 indicator.layer.borderWidth = 1 indicator.layer.borderColor = UIColor.lightGray.cgColor view.addSubview(indicator) indicator.bringSubviewToFront(view) } public func reloadNotesTable(completion: (() -> ())? = nil) { isActiveTableUpdating = true searchQueue.cancelAllOperations() setNavTitle(qty: "∞") searchQueue.cancelAllOperations() let operation = BlockOperation() operation.addExecutionBlock { [weak self] in guard let self = self else { completion?() return } self.accessTime = DispatchTime.now() let source = self.storage.noteList var notes = [Note]() for note in source { if operation.isCancelled { break } if Storage.shared().searchQuery.isFit(note: note) { notes.append(note) } } if let project = Storage.shared().searchQuery.projects.first, project.isLocked() { notes.removeAll() } var modifiedNotesList = [Note]() if !notes.isEmpty { modifiedNotesList = self.storage.sortNotes(noteList: notes) } if operation.isCancelled { completion?() return } DispatchQueue.main.async { self.setNavTitle(qty: String(notes.count)) if DispatchTime.now() < self.accessTime { completion?() return } self.notesTable.notes = modifiedNotesList self.notesTable.reloadData() if let note = self.delayedInsert { self.notesTable.insertRows(notes: [note]) self.delayedInsert = nil } self.isActiveTableUpdating = false completion?() } } self.searchQueue.addOperation(operation) } public func updateNotesCounter() { DispatchQueue.main.async { self.setNavTitle(qty: String(self.notesTable.notes.count)) } } public func isNoteInsertionAllowed() -> Bool { if let searchBar = getSearchBar() { return !searchBar.isFirstResponder } return true } public func getSearchBar() -> UISearchBar? { return navigationItem.searchController?.searchBar } @objc func newButtonAction() { if let project = sidebarTableView.getSidebarProjects()?.first, project.isEncrypted, project.password == nil { unlockProject(selectedProject: project, createNote: true) return } createNote(content: nil) } @objc public func closeNews() { UIImpactFeedbackGenerator(style: .medium).impactOccurred() newsPopup?.removeFromSuperview() newsOverlay?.removeFromSuperview() // mark as read UserDefaultsManagement.lastNews = storage.getNewsDate() } public func createNote(content: String? = nil, pasteboard: Bool = false) { var currentProject: Project if let project = storage.getProjects().first { currentProject = project } else { return } if let item = self.sidebarTableView.getSidebarItem(), let project = item.project, !project.isTrash, !project.isVirtual { currentProject = project } let note = Note(name: "", project: currentProject) if let content = content { note.content = NSMutableAttributedString(string: content) } var selectedRange: NSRange? if pasteboard { if let image = UIPasteboard.general.image, let data = image.jpegData(compressionQuality: 1), let imagePath = ImagesProcessor.writeFile(data: data, note: note) { let string = "![](\(imagePath))\n\n" note.content = NSMutableAttributedString(string: string) selectedRange = NSRange(location: string.count, length: 0) } else if let content = UIPasteboard.general.string { note.content = NSMutableAttributedString(string: content) selectedRange = NSRange(location: content.count, length: 0) } } if note.save() { Storage.shared().add(note) } let evc = UIApplication.getEVC() evc.note = note evc.fill(note: note, selectedRange: selectedRange) openEditorViewController() if self.isActiveTableUpdating { self.delayedInsert = note } else { notesTable.insertRows(notes: [note]) notesTable.scrollTo(note: note) } DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { let evc = UIApplication.getEVC() if note.previewState { evc.togglePreview() } evc.editArea.becomeFirstResponder() if let password = note.project.password { if note.encrypt(password: password) { if note.unLock(password: password) { note.password = password } } } } } public func openEditorViewController() { navigationController?.interactivePopGestureRecognizer?.delegate = nil if let controllers = navigationController?.viewControllers { for controller in controllers { if let _ = controller as? EditorViewController { return } } } let evc = UIApplication.getEVC() editorViewController = evc navigationController?.pushViewController(evc, animated: true) } public func popViewController() { navigationController?.popViewController(animated: true) } public func savePasteboard(note: Note) { let pboard = UIPasteboard.general let pasteboardString: String? = pboard.string if let content = pasteboardString { note.content = NSMutableAttributedString(string: content) } if let image = pboard.image { if let data = image.jpegData(compressionQuality: 1) { guard let imagePath = ImagesProcessor.writeFile(data: data, note: note) else { return } note.content = NSMutableAttributedString(string: "![](\(imagePath))\n\n") } } if note.save() { Storage.shared().add(note) } } public func importSavedInSharedExtension() { var notes = [Note]() for url in UserDefaultsManagement.importURLs { if let note = storage.importNote(url: url) { notes.append(note) } } notesTable.insertRows(notes: notes) UserDefaultsManagement.importURLs = [] } @objc func preferredContentSizeChanged() { view.setNeedsLayout() view.layoutIfNeeded() } @objc func rotated() { guard isLandscape != nil else { isLandscape = UIDevice.current.orientation.isLandscape return } let isLand = UIDevice.current.orientation.isLandscape if let landscape = self.isLandscape, landscape != isLand, !UIDevice.current.orientation.isFlat { isLandscape = isLand DispatchQueue.main.async { self.loadNews() self.resizeSidebar(withAnimation: true) } } } @objc func willExitForeground() { importSavedInSharedExtension() } private var swipeStartLeadingConstant: CGFloat = 0 @objc func handleSidebarSwipe(_ swipe: UIPanGestureRecognizer) { let notchWidth = getLeftInset() let translation = swipe.translation(in: notesTable) if swipe.state == .began { maxSidebarWidth = calculateLabelMaxWidth() sidebarTableView.isUserInteractionEnabled = true if !UserDefaultsManagement.sidebarIsOpened { self.sidebarTableLeadingConstraint.constant = -self.maxSidebarWidth self.sidebarTableWidth.constant = self.maxSidebarWidth self.notesTableLeadingConstraint.constant = 0 leftPreSafeArea.backgroundColor = UIColor.sidebar notesTable.dragInteractionEnabled = false sidebarTableView.isUserInteractionEnabled = false swipeStartLeadingConstant = 0 } else { let correctLeading = self.maxSidebarWidth + notchWidth self.sidebarTableLeadingConstraint.constant = 0 self.sidebarTableWidth.constant = self.maxSidebarWidth self.notesTableLeadingConstraint.constant = correctLeading notesTable.dragInteractionEnabled = true sidebarTableView.isUserInteractionEnabled = true swipeStartLeadingConstant = correctLeading } return } if swipe.state == .changed { let newLeading = swipeStartLeadingConstant + translation.x let sidebarRange = maxSidebarWidth + notchWidth guard newLeading >= 0 && newLeading <= sidebarRange else { return } UIView.animate(withDuration: 0.075, delay: 0.0, options: .beginFromCurrentState, animations: { self.notesTableLeadingConstraint.constant = newLeading let sidebarOffset = max(0, newLeading - notchWidth) let sidebarProgress = min(sidebarOffset, self.maxSidebarWidth) / self.maxSidebarWidth self.sidebarTableLeadingConstraint.constant = -self.maxSidebarWidth * (1 - sidebarProgress) self.view.layoutIfNeeded() }) return } if swipe.state == .ended { if translation.x > 0 { showSidebar() } if translation.x < 0 { hideSidebar() } } } private func initSidebar() { if UserDefaultsManagement.sidebarIsOpened { self.sidebarTableLeadingConstraint.constant = 0 self.notesTableLeadingConstraint.constant = self.maxSidebarWidth + getLeftInset() self.notesTable.dragInteractionEnabled = true self.sidebarTableView.isUserInteractionEnabled = true } else { self.notesTableLeadingConstraint.constant = 0 self.sidebarTableLeadingConstraint.constant = -self.maxSidebarWidth self.sidebarTableWidth.constant = 0 // blue/blck pre safe area leftPreSafeArea.backgroundColor = UIColor.sidebar self.notesTable.dragInteractionEnabled = false self.sidebarTableView.isUserInteractionEnabled = false } } private func showSidebar() { let leftInset = getLeftInset() UIView.animate(withDuration: 0.2, delay: 0.0, options: .init(), animations: { self.notesTableLeadingConstraint.constant = self.maxSidebarWidth + leftInset self.sidebarTableLeadingConstraint.constant = 0 self.sidebarTableWidth.constant = self.maxSidebarWidth self.view.layoutIfNeeded() }) { _ in UserDefaultsManagement.sidebarIsOpened = true self.notesTable.dragInteractionEnabled = true self.sidebarTableView.isUserInteractionEnabled = true self.leftPreSafeArea.backgroundColor = UIColor.sidebar } } private func hideSidebar() { UIView.animate(withDuration: 0.2, delay: 0.0, options: .init(), animations: { self.notesTableLeadingConstraint.constant = 0 self.sidebarTableLeadingConstraint.constant = -self.maxSidebarWidth self.view.layoutIfNeeded() }) { _ in UserDefaultsManagement.sidebarIsOpened = false self.notesTable.dragInteractionEnabled = false self.sidebarTableView.isUserInteractionEnabled = false // white pre safe area self.leftPreSafeArea.backgroundColor = .whiteBlack } } @objc func keyboardWillShow(notification: NSNotification) { if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { //notesTableBottomContraint.constant = keyboardSize.height sidebarTableBottomConstraint.constant = keyboardSize.height } } @objc func keyboardWillHide(notification: NSNotification) { //notesTableBottomContraint.constant = 0 sidebarTableBottomConstraint.constant = 0 } public func refreshTextStorage(note: Note) { DispatchQueue.main.async { UIApplication.getEVC().fill(note: note) } } private func calculateLabelMaxWidth() -> CGFloat { var width = CGFloat(0) var font = UIFont(name: "HelveticaNeue-BoldItalic", size: 15) let fontMetrics = UIFontMetrics(forTextStyle: .title3) font = fontMetrics.scaledFont(for: font!) let settings = NSLocalizedString("Settings", comment: "Sidebar settings") let untagged = NSLocalizedString("Untagged", comment: "Sidebar settings") let inbox = NSLocalizedString("Inbox", comment: "Inbox in sidebar") let notes = NSLocalizedString("Notes", comment: "Notes in sidebar") let todo = NSLocalizedString("Todo", comment: "Todo in sidebar") let trash = NSLocalizedString("Trash", comment: "Trash in sidebar") var sidebarItems = [String]() var tags = [String]() if let project = storage.searchQuery.projects.first { tags = sidebarTableView.getAllTags(projects: [project]) } sidebarItems = tags + Storage.shared().getProjects().map({ $0.label }) + [settings, inbox, notes, todo, trash, untagged] for item in sidebarItems { guard let font = font else { continue } let labelWidth = (item as NSString).size(withAttributes: [.font: font]).width + 60 if labelWidth < (view.frame.size.width / 2) { if labelWidth > width { width = labelWidth } } else { width = view.frame.size.width / 2 } } return width } public func unLock(notes: [Note], completion: @escaping ([Note]?) -> ()) { getMasterPassword() { password in self.unLock(notes: notes, completion: completion, password: password) } } public func unLock(notes: [Note], completion: @escaping ([Note]?) -> (), password: String) { for note in notes { var success = [Note]() if note.unLock(password: password) { note.password = password success.append(note) } DispatchQueue.main.async { self.notesTable.reloadRowForce(note: note) } completion(success) } } public func toggleNotesLock(notes: [Note]) { var notes = notes notes = lockUnlocked(notes: notes) guard notes.count > 0 else { return } getMasterPassword() { password in for note in notes { if note.container == .encryptedTextPack { if note.unLock(password: password) { note.password = password DispatchQueue.main.async { self.notesTable.reloadRowForce(note: note) UIApplication.getEVC().fill(note: note) UIApplication.getVC().openEditorViewController() } } } else { if note.encrypt(password: password) { note.password = nil DispatchQueue.main.async { self.notesTable.reloadRowForce(note: note) } } } } } } private func lockUnlocked(notes: [Note]) -> [Note] { var notes = notes var isFirst = true for note in notes { if note.isUnlocked() { if note.lock() && isFirst { note.password = nil notesTable.reloadRowForce(note: note) } notes.removeAll { $0 === note } } isFirst = false } return notes } public func getMasterPassword(isUnlock: Bool = false, completion: @escaping (String) -> ()) { let context = LAContext() context.localizedFallbackTitle = NSLocalizedString("Enter Master Password", comment: "") var passwordExist = false do { let item = KeychainPasswordItem(service: KeychainConfiguration.serviceName, account: "Master Password") let password = try item.readPassword() passwordExist = password.count > 0 } catch {/*_*/} guard passwordExist && context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) else { masterPasswordPrompt(completion: completion) return } context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "To access master password") { (success, evaluateError) in if !success { self.masterPasswordPrompt(completion: completion) return } do { let item = KeychainPasswordItem(service: KeychainConfiguration.serviceName, account: "Master Password") let password = try item.readPassword() completion(password) return } catch { print(error) } self.masterPasswordPrompt(completion: completion) } } private func masterPasswordPrompt(completion: @escaping (String) -> ()) { DispatchQueue.main.async { let title = NSLocalizedString("Master password:", comment: "") let alertController = UIAlertController(title: title, message: nil, preferredStyle: .alert) alertController.addTextField(configurationHandler: { [] (textField: UITextField) in textField.placeholder = "mast3r passw0rd" }) let confirmAction = UIAlertAction(title: "OK", style: .default) { (_) in guard let password = alertController.textFields?[0].text, password.count > 0 else { return } completion(password) } let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in } alertController.addAction(confirmAction) alertController.addAction(cancelAction) self.present(alertController, animated: true) { alertController.textFields![0].selectAll(nil) } } } public func unlockPasswordPrompt(completion: @escaping (String) -> ()) { DispatchQueue.main.async { let title = NSLocalizedString("Password:", comment: "") let alertController = UIAlertController(title: title, message: nil, preferredStyle: .alert) alertController.addTextField(configurationHandler: { [] (textField: UITextField) in textField.placeholder = "note passw0rd" textField.isSecureTextEntry = true }) let confirmAction = UIAlertAction(title: "OK", style: .default) { (_) in guard let password = alertController.textFields?[0].text, password.count > 0 else { return } completion(password) } let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in } alertController.addAction(confirmAction) alertController.addAction(cancelAction) self.present(alertController, animated: true) { alertController.textFields![0].selectAll(nil) } } } public func resizeSidebar(withAnimation: Bool = false) { let leftInset = getLeftInset() let width = calculateLabelMaxWidth() maxSidebarWidth = width guard UserDefaultsManagement.sidebarIsOpened else { return } if maxSidebarWidth > view.frame.size.width { maxSidebarWidth = view.frame.size.width / 2 } if (withAnimation) { UIView.animate(withDuration: 0.3, delay: 0, options: .beginFromCurrentState, animations: { let width = self.maxSidebarWidth self.notesTableLeadingConstraint.constant = width + leftInset self.sidebarTableLeadingConstraint.constant = 0 self.sidebarTableWidth.constant = width }) { _ in } } else { notesTableLeadingConstraint.constant = maxSidebarWidth + leftInset sidebarTableWidth.constant = maxSidebarWidth } } public func restoreLastController() { guard !self.storage.isCrashedLastTime, let noteURL = UserDefaultsManagement.currentNote, FileManager.default.fileExists(atPath: noteURL.path) else { return } let note = Storage.shared().addNote(url: noteURL) guard !note.isEncrypted() else { return } note.loadPreviewState() UIApplication.getVC().openEditorViewController() DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { UIApplication.getEVC().fill(note: note) UIApplication.getEVC().configureNavMenu() if UserDefaultsManagement.currentEditorState == true, let selectedRange = UserDefaultsManagement.currentRange, !note.previewState, selectedRange.upperBound <= note.content.length { UIApplication.getEVC().editArea.becomeFirstResponder() UIApplication.getEVC().editArea.selectedRange = selectedRange DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { UIApplication.getEVC().editArea.scrollRangeToVisible(selectedRange) } } } UserDefaultsManagement.currentNote = nil } public func reloadDatabase() { Storage.instance = nil storage = Storage.shared() sidebarTableView.reloadSidebar() initialLoadingState = false viewDidLoad() } public func enableLockedProject() { lockedProject.isHidden = false clean() } public func disableLockedProject() { lockedProject.isHidden = true } public func clean() { notesTable.notes.removeAll() notesTable.reloadData() } public func showAlert(message: String) { DispatchQueue.main.async { let title = NSLocalizedString("Web sharing error", comment: "") let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) let confirmAction = UIAlertAction(title: "OK", style: .default) alertController.addAction(confirmAction) self.present(alertController, animated: true) } } public func buildSearchQuery() { let searchQuery = SearchQuery() var projects = [Project]() var tags = [String]() var type: SidebarItemType? if let sidebarTableView = self.sidebarTableView, let indexPaths = sidebarTableView.indexPathsForSelectedRows { for indexPath in indexPaths { let item = sidebarTableView.sidebar.items[indexPath.section][indexPath.row] if let project = item.project, !project.isVirtual { projects.append(project) } if item.type == .Tag { tags.append(item.name) } if item.type == .All || item.type == .Untagged || item.type == .Todo || item.type == .Trash || item.type == .Inbox { type = item.type } } } if projects.count == 0 && type == nil { type = .All } let filter = getSearchBar()?.text ?? "" searchQuery.projects = projects searchQuery.tags = tags searchQuery.setFilter(filter) if let type = type { searchQuery.setType(type) } self.storage.setSearchQuery(value: searchQuery) } } extension ViewController : UIPopoverPresentationControllerDelegate { func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle { return .none } } extension UIApplication { public func runInBackground(_ closure: @escaping () -> Void, expirationHandler: (() -> Void)? = nil) { let taskID: UIBackgroundTaskIdentifier if let expirationHandler = expirationHandler { taskID = self.beginBackgroundTask(expirationHandler: expirationHandler) } else { taskID = self.beginBackgroundTask(expirationHandler: { }) } DispatchQueue.global(qos: .background).sync { closure() } self.endBackgroundTask(taskID) } } ================================================ FILE: FSNotes iOS/fr.lproj/Main.storyboard ================================================ ================================================ FILE: FSNotes iOS/nl-NL.lproj/Main.storyboard ================================================ ================================================ FILE: FSNotes iOS/pt-PT.lproj/Main.storyboard ================================================ ================================================ FILE: FSNotes iOS/ru.lproj/InfoPlist.strings ================================================ "NSCameraUsageDescription" = "Требуется прикрепить фото к заметкам"; "NSFaceIDUsageDescription" = "Шифрование и дешифрование с помощью FaceID"; "NSLocationWhenInUseUsageDescription" = "Запрошено при прикреплении фотографии"; "NSPhotoLibraryAddUsageDescription" = "Требуется разрешение на запись изображений в приложении для фотографий"; "NSPhotoLibraryUsageDescription" = "Требуется прикрепление изображений к заметкам"; ================================================ FILE: FSNotes iOS/ru.lproj/LaunchScreen.strings ================================================ ================================================ FILE: FSNotes iOS/ru.lproj/Localizable.strings ================================================ /* Settings */ "+" = "+"; /* No comment provided by engineer. */ "..." = "..."; /* Settings */ "Add External Folder" = "Внешняя директория"; /* Settings */ "Advanced" = "Advanced"; /* Archive in sidebar */ "Archive" = "Архив"; /* No comment provided by engineer. */ "Are you sure you want to delete all versions of this note?" = "Вы уверены, что хотите удалить все версии этой заметки?"; /* No comment provided by engineer. */ "Are you sure you want to delete the history of all notes?" = "Вы уверены, что хотите удалить историю всех заметок?"; /* No comment provided by engineer. */ "Are you sure you want to delete this image?" = "Вы уверены, что хотите удалить это изображение?"; /* Settings */ "Auto Rename By Title" = "По заголовку"; /* Settings */ "Autocorrection" = "Aвтоисправление"; /* Settings */ "build" = "билд"; /* No comment provided by engineer. */ "Cancel" = "Отменить"; /* No comment provided by engineer. */ "Change Creation Date" = "Change Creation Date"; /* Settings */ "Check Spelling" = "Check Spelling"; /* No comment provided by engineer. */ "Code" = "Блок кода"; /* Settings */ "Code Block Live Highlighting" = "Code Block Live Highlighting"; /* Settings */ "Code Theme" = "Тема кода"; /* No comment provided by engineer. */ "Compatible with DayOne JSON (zip), Bear and Ulysses (textbundle), markdown, txt, rtf." = "Совместимо с DayOne JSON (zip), Bear and Ulysses (textbundle), markdown, txt, rtf."; /* Settings */ "Container" = "Контейнер"; /* No comment provided by engineer. */ "Copy Plain Text" = "Copy Plain Text"; /* Main view popover table */ "Create Folder" = "Create Folder"; /* No comment provided by engineer. */ "Create folder:" = "Создать папку:"; /* No comment provided by engineer. */ "Create Web Page" = "Create Web Page"; /* No comment provided by engineer. */ "Creation Date" = "Creation Date"; /* Main view popover table */ "Decrypt" = "Decrypt"; /* Settings */ "Default Keyboard" = "Default Keyboard"; /* Table row action */ "Delete" = "Удалить"; /* No comment provided by engineer. */ "Delete Web Page" = "Delete Web Page"; /* No comment provided by engineer. */ "Documents" = "Документы"; /* No comment provided by engineer. */ "Done" = "Done"; /* No comment provided by engineer. */ "Duplicate" = "Создать копию"; /* Settings */ "Dynamic Type" = "Динамический шрифт"; /* Settings */ "Editor" = "Редактор"; /* Main view popover table */ "Empty Bin" = "Очистить корзину"; /* Main view popover table */ "Encrypt" = "Encrypt"; /* No comment provided by engineer. */ "Enter folder name" = "Введите название папки"; /* No comment provided by engineer. */ "Enter Master Password" = "Введите пароль"; /* No comment provided by engineer. */ "Enter new tag name" = "Введите название тега"; /* No comment provided by engineer. */ "Enter note name" = "Введите название заметки"; /* Settings */ "Extension" = "Расширение"; /* Settings */ "Family" = "Семейство шрифтов"; /* No comment provided by engineer. */ "File with this name already exist" = "Файл с таким именем уже существует"; /* Settings */ "Files" = "Files"; /* Settings */ "Files Naming" = "Именование файлов"; /* No comment provided by engineer. */ "Folder name:" = "Название папки:"; /* No comment provided by engineer. */ "Folder with this name already exist" = "Папка с таким именем уже существует"; /* Settings */ "Folders" = "Folders"; /* Settings */ "Font" = "Шрифт"; /* Settings */ "Font Family" = "Семейство шрифтов"; /* Settings */ "Font Size" = "Font Size"; /* Settings */ "Format: Untitled Note" = "Формат: Untitled Note"; /* Settings */ "Format: yyyy-MM-dd hh.mm.ss a" = "Формат: yyyy-MM-dd hh.mm.ss a"; /* Settings */ "Format: yyyyMMddHHmmss" = "Формат: yyyyMMddHHmmss"; /* Settings */ "FSNotes" = "FSNotes"; /* Settings */ "General" = "Основные"; /* Settings */ "Git" = "Git"; /* Main view popover table */ "Git Add/commit/push" = "Git Add/commit/push"; /* Main view popover table */ "Git Settings" = "Git Settings"; /* No comment provided by engineer. */ "History" = "История"; /* Settings */ "iCloud Drive" = "iCloud Drive"; /* Settings */ "Icon" = "Icon"; /* No comment provided by engineer. */ "Images source:" = "Источник изображения:"; /* Settings */ "Import Notes" = "Import Notes"; /* Inbox in sidebar */ "Inbox" = "Входящие"; /* No comment provided by engineer. */ "Invalid Password" = "Неверный пароль"; /* Settings */ "Library" = "Library"; /* Settings */ "Line Spacing" = "Межстрочный интервал"; /* Settings */ "Live Images Preview" = "Live Images Preview"; /* No comment provided by engineer. */ "Lock" = "Заблокировать"; /* Settings */ "Master" = "Master"; /* No comment provided by engineer. */ "Master password:" = "Мастер пароль:"; /* Settings */ "MathJax" = "MathJax"; /* No comment provided by engineer. */ "Modification Date" = "Modification Date"; /* Table row action */ "More" = "Ещё"; /* Move view */ "Move" = "Переместить"; /* No comment provided by engineer. */ "New Note" = "New Note"; /* No comment provided by engineer. */ "None" = "Умолчанию"; /* Notes in sidebar */ "Notes" = "Заметки"; /* No comment provided by engineer. */ "Notes List" = "Notes List"; /* Main view popover table */ "Open in Files.app" = "Открыть в Files.app"; /* Document opened */ "Open Note" = "Open Note"; /* No comment provided by engineer. */ "Passphrase" = "Passphrase"; /* No comment provided by engineer. */ "Password" = "Password"; /* No comment provided by engineer. */ "Password has been successfully changed" = "Password has been successfully changed"; /* No comment provided by engineer. */ "Password:" = "Password:"; /* No comment provided by engineer. */ "Photos" = "Фото"; /* No comment provided by engineer. */ "Picture removing" = "Удаление изображения"; /* Table row action */ "Pin" = "Прикрепить"; /* No comment provided by engineer. */ "Please enter valid password" = "Введите действующий пароль"; /* No comment provided by engineer. */ "Please try again" = "Please try again"; /* No comment provided by engineer. */ "Private Key" = "Private Key"; /* No comment provided by engineer. */ "Project removing ❌" = "Удаление проекта ❌"; /* No comment provided by engineer. */ "Public Key (optional)" = "Public Key (optional)"; /* No comment provided by engineer. */ "Pull (every 30 sec)" = "Pull (every 30 sec)"; /* No comment provided by engineer. */ "Remove Encryption" = "Remove Encryption"; /* Main view popover table */ "Remove Folder" = "Remove Folder"; /* Main view popover table */ "Remove Tag" = "Remove Tag"; /* No comment provided by engineer. */ "Rename" = "Переименовать"; /* Main view popover table */ "Rename Folder" = "Rename Folder"; /* Popover table */ "Rename folder:" = "Переименовать папку:"; /* No comment provided by engineer. */ "Rename note:" = "Переименовать заметку:"; /* Main view popover table */ "Rename Tag" = "Rename Tag"; /* Popover table */ "Rename tag:" = "Переименовать тег:"; /* No comment provided by engineer. */ "Save" = "Save"; /* No comment provided by engineer. */ "Save Clipboard" = "Save Clipboard"; /* No comment provided by engineer. */ "Save Revision" = "Save Revision"; /* No comment provided by engineer. */ "Saved versions" = "Сохраненные версии"; /* No comment provided by engineer. */ "Search or create" = "Найти или создать"; /* No comment provided by engineer. */ "Search or Create" = "Search or Create"; /* Settings */ "Security" = "Security"; /* Main view popover table */ "Select" = "Выбрать"; /* Sidebar settings */ "Settings" = "Наcтройки"; /* No comment provided by engineer. */ "Share" = "Поделиться"; /* No comment provided by engineer. */ "Show Folder in Library" = "Show Folder in Library"; /* No comment provided by engineer. */ "Show Notes in \"Notes\" and \"Todo\"" = "Show Notes in \"Notes\" and \"Todo\""; /* Settings */ "Sort By" = "Sort By"; /* Settings */ "SoulverCore" = "SoulverCore"; /* Settings */ "Storage" = "Хранилище"; /* Settings */ "Support" = "Поддержка"; /* Settings */ "Thanks" = "Thanks"; /* Settings */ "Theme" = "Тема"; /* No comment provided by engineer. */ "Tip: To use old notes, you must decrypt them separately with the old key and encrypt them again." = "Tip: To use old notes, you must decrypt them separately with the old key and encrypt them again."; /* No comment provided by engineer. */ "Title" = "Заголовку"; /* Sidebar items - Todo in sidebar */ "Todo" = "Todo"; /* Sidebar label - Trash in sidebar */ "Trash" = "Корзина"; /* Settings */ "Twitter" = "Twitter"; /* No comment provided by engineer. */ "Unlock" = "Разблокировать"; /* Table row action */ "Unpin" = "Unpin"; /* No comment provided by engineer. */ "Untagged" = "Без тегов"; /* No comment provided by engineer. */ "Update" = "Update"; /* No comment provided by engineer. */ "Use First Line as Title" = "Use First Line as Title"; /* No comment provided by engineer. */ "Use Inline Tags" = "Use Inline Tags"; /* No comment provided by engineer. */ "Verify Password" = "Verify Password"; /* Settings */ "Version" = "Версия"; /* No comment provided by engineer. */ "View" = "Вид"; /* Main view popover table */ "View Settings" = "View Settings"; /* No comment provided by engineer. */ "Visibility" = "Видимость"; /* No comment provided by engineer. */ "Web sharing error" = "Web sharing error"; /* Settings */ "Website" = "Website"; /* No comment provided by engineer. */ "Wrong password" = "Wrong password"; /* No comment provided by engineer. */ "Wrong repeated password" = "Wrong repeated password"; /* No comment provided by engineer. */ "Сlearing history" = "Очистка истории"; /* No comment provided by engineer. */ "✅ - " = "✅ - "; ================================================ FILE: FSNotes iOS/ru.lproj/Main.storyboard ================================================ ================================================ FILE: FSNotes iOS/uk.lproj/Main.storyboard ================================================ ================================================ FILE: FSNotes iOS Share/.bartycrouch.toml ================================================ [update] tasks = ["interfaces", "code", "transform", "normalize"] [update.interfaces] paths = ["."] defaultToBase = false ignoreEmptyStrings = false unstripped = false [update.code] codePaths = ["."] localizablePaths = ["."] defaultToKeys = true additive = true unstripped = false plistArguments = true [update.transform] codePaths = ["."] localizablePaths = ["."] transformer = "foundation" supportedLanguageEnumPath = "." typeName = "BartyCrouch" translateMethodName = "translate" [update.normalize] paths = ["."] sourceLocale = "en" harmonizeWithSource = true sortByKeys = true [lint] paths = ["."] duplicateKeys = true emptyValues = true ================================================ FILE: FSNotes iOS Share/FSNotes iOS Share.entitlements ================================================ com.apple.developer.icloud-container-identifiers iCloud.co.fluder.fsnotes com.apple.developer.icloud-services CloudDocuments com.apple.developer.ubiquity-container-identifiers iCloud.co.fluder.fsnotes com.apple.developer.ubiquity-kvstore-identifier $(TeamIdentifierPrefix)$(CFBundleIdentifier) com.apple.security.application-groups group.es.fsnot.user.defaults ================================================ FILE: FSNotes iOS Share/Info.plist ================================================ NSPrivacyAccessedAPITypes NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategoryFileTimestamp NSPrivacyAccessedAPITypeReasons DDA9.1 NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategoryUserDefaults NSPrivacyAccessedAPITypeReasons CA92.1 CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName FSNotes CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType XPC! CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion $(CURRENT_PROJECT_VERSION) NSExtension NSExtensionAttributes NSExtensionActivationRule SUBQUERY ( extensionItems, $extensionItem, SUBQUERY ( $extensionItem.attachments, $attachment, ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image" ).@count > 0 ).@count == 1 OR SUBQUERY ( extensionItems, $extensionItem, SUBQUERY ( $extensionItem.attachments, $attachment, ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" ).@count == 1 ).@count == 1 OR SUBQUERY ( extensionItems, $extensionItem, SUBQUERY ( $extensionItem.attachments, $attachment, ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.plain-text" ).@count == 1 ).@count == 1 NSExtensionPointIdentifier com.apple.share-services NSExtensionPrincipalClass ShareViewController ================================================ FILE: FSNotes iOS Share/Localizable.xcstrings ================================================ { "sourceLanguage" : "en", "strings" : { "Inbox" : { "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", "value" : "Příchozí" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इनबॉक्स" } } } }, "New note" : { "localizations" : { "ar-IQ" : { "stringUnit" : { "state" : "translated", "value" : "ملاحظة جديدة" } }, "cs" : { "stringUnit" : { "state" : "translated", "value" : "Nová poznámka" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Speichern" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nueva nota" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nouvelle note" } }, "he" : { "stringUnit" : { "state" : "translated", "value" : "New note" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नया नोट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuova nota" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新規ノート" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새 메모" } }, "nl-NL" : { "stringUnit" : { "state" : "translated", "value" : "Nieuwe notitie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nova nota" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Nova nota" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сохранить" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зберегти" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "新笔记" } } } } }, "version" : "1.0" } ================================================ FILE: FSNotes iOS Share/MainInterface.storyboard ================================================ ================================================ FILE: FSNotes iOS Share/NSMutableAttributedString+.swift ================================================ // // NSMutableAttributedString+.swift // FSNotes // // Created by Oleksandr Hlushchenko on 14.11.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import Foundation #if os(OSX) import AppKit #else import UIKit #endif extension NSMutableAttributedString { convenience init(url: URL, title: String = "", path: String) { self.init() } public func unloadImagesAndFiles() -> NSMutableAttributedString { return self } public func loadImagesAndFiles(note: Note) { } public func unloadTasks() -> NSMutableAttributedString { return self } public func loadTasks() { } public func unloadAttachments() -> NSMutableAttributedString { return self } public func loadAttachments(_ note: Note) -> NSMutableAttributedString { return self } public func replaceTag(name: String, with replaceString: String) { } public func getImagesAndFiles() -> [(url: URL, title: String, path: String)] { return [] } public func getMeta(at location: Int) -> (url: URL, title: String, path: String)? { return nil } } ================================================ FILE: FSNotes iOS Share/ShareViewController.swift ================================================ // // ShareViewController.swift // FSNotes iOS Share // // Created by Oleksandr Glushchenko on 3/18/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import UIKit import MobileCoreServices import Social import UniformTypeIdentifiers @objc(ShareViewController) class ShareViewController: SLComposeServiceViewController { // MARK: - Properties private var hasImages = false private var urlPreview: String? // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() configureNavigationBar() } // MARK: - Configuration private func configureNavigationBar() { guard let navigationBar = navigationController?.navigationBar, let rightButton = navigationBar.topItem?.rightBarButtonItem else { return } rightButton.title = NSLocalizedString("New note", comment: "") navigationBar.tintColor = .mainTheme let titleLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 50, height: 20)) titleLabel.text = "FSNotes" titleLabel.font = UserDefaultsManagement.noteFont.bold().withSize(18) navigationBar.topItem?.titleView = titleLabel } // MARK: - Preview override func loadPreviewView() -> UIView! { urlPreview = textView.text guard let inputItems = extensionContext?.inputItems as? [NSExtensionItem] else { return UIView() } processInputItems(inputItems) return hasImages ? super.loadPreviewView() : UIView() } private func processInputItems(_ items: [NSExtensionItem]) { for item in items { guard let attachments = item.attachments else { continue } for attachment in attachments { if checkForImages(in: attachment) { hasImages = true textView.text = "" return } loadURLIfNeeded(from: attachment) } } } private func checkForImages(in attachment: NSItemProvider) -> Bool { return attachment.hasItemConformingToTypeIdentifier(kUTTypeImage as String) || attachment.hasItemConformingToTypeIdentifier(kUTTypeJPEG as String) } private func loadURLIfNeeded(from attachment: NSItemProvider) { guard attachment.hasItemConformingToTypeIdentifier(kUTTypeURL as String) else { return } attachment.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil) { [weak self] url, error in guard let self = self, let url = url as? URL, error == nil else { return } self.handleLoadedURL(url) } } private func handleLoadedURL(_ url: URL) { if url.absoluteString.starts(with: "file:///") { loadFileContent(from: url) } else { updateTextViewWithURL(url) } } private func loadFileContent(from url: URL) { guard let fileData = try? Data(contentsOf: url), let text = String(data: fileData, encoding: .utf8) else { return } DispatchQueue.main.async { [weak self] in self?.textView.text = text } } private func updateTextViewWithURL(_ url: URL) { DispatchQueue.main.async { [weak self] in guard let self = self else { return } let preview = self.urlPreview ?? "" self.textView.text = "\(preview)\n\n\(url.absoluteString)".trimmingCharacters(in: .whitespacesAndNewlines) } } // MARK: - Validation & Post override func isContentValid() -> Bool { return true } override func didSelectPost() { saveNote() } override func configurationItems() -> [Any]! { return [] } // MARK: - Save Note private func saveNote() { guard let inputItems = extensionContext?.inputItems as? [NSExtensionItem] else { closeExtension() return } let note = createNote() processAttachments(from: inputItems, note: note) } private func createNote() -> Note { let note = Note() Storage.shared().add(note) var urls = UserDefaultsManagement.importURLs urls.insert(note.url, at: 0) UserDefaultsManagement.importURLs = urls return note } private func appendTextContent(to note: Note) { guard !textView.text.isEmpty else { return } note.append(string: NSMutableAttributedString(string: textView.text)) } private func processAttachments(from items: [NSExtensionItem], note: Note) { var imageProviders: [NSItemProvider] = [] for item in items { guard let attachments = item.attachments else { continue } for provider in attachments { if provider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) { imageProviders.append(provider) } else if provider.hasItemConformingToTypeIdentifier(kUTTypeURL as String) { processURLAttachment(note: note) return } else if provider.hasItemConformingToTypeIdentifier(kUTTypeText as String) { processTextAttachment(note: note) return } } } if imageProviders.isEmpty { closeExtension() } else { processImageAttachments(imageProviders, note: note) } } private func processImageAttachments(_ providers: [NSItemProvider], note: Note) { let totalCount = providers.count var processedCount = 0 for provider in providers { provider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: [:]) { [weak self] data, error in guard let self = self, error == nil else { processedCount += 1 if processedCount == totalCount { self?.finalizeNoteSave(note) } return } let imageData = self.extractImageData(from: data) let url = data as? URL if let imageData = imageData { note.append(image: imageData, url: url) } processedCount += 1 if processedCount == totalCount { self.finalizeNoteSave(note) } } } } private func extractImageData(from data: Any?) -> Data? { if let data = data as? Data { return data } else if let image = data as? UIImage { return image.jpegData(compressionQuality: 1) } else if let url = data as? URL { return try? Data(contentsOf: url) } return nil } private func processURLAttachment(note: Note) { guard !hasImages, let contentText = contentText else { closeExtension() return } if let url = URL(string: contentText), let data = try? Data(contentsOf: url), let image = UIImage(data: data), image.size.width > 0 { note.append(image: data) } else { appendContentWithPrefix(contentText, to: note) } finalizeNoteSave(note) } private func processTextAttachment(note: Note) { guard !hasImages, let contentText = contentText else { closeExtension() return } appendContentWithPrefix(contentText, to: note) finalizeNoteSave(note) } private func appendContentWithPrefix(_ content: String, to note: Note) { let prefix = note.content.length == 0 ? "" : "\n\n" let string = NSMutableAttributedString(string: "\(prefix)\(content)") note.append(string: string) } private func finalizeNoteSave(_ note: Note) { if note.saveSimple() { Storage.shared().add(note) } closeExtension() } private func closeExtension() { extensionContext?.completeRequest(returningItems: extensionContext?.inputItems, completionHandler: nil) } } ================================================ FILE: FSNotes iOS Share/es.lproj/Localizable.strings ================================================ /* No comment provided by engineer. */ "Append to" = ""; /* No comment provided by engineer. */ "Choose for append" = ""; /* No comment provided by engineer. */ "New note" = ""; /* No comment provided by engineer. */ "Project" = ""; ================================================ FILE: FSNotes iOS Share/pt.lproj/InfoPlist.strings ================================================ /* Bundle display name */ "CFBundleDisplayName" = "FSNotes"; /* Bundle name */ "CFBundleName" = "مشاركة"; ================================================ FILE: FSNotes iOS Share/ru.lproj/InfoPlist.strings ================================================ ================================================ FILE: FSNotes iOS Share/ru.lproj/Localizable.strings ================================================ /* No comment provided by engineer. */ "Append to" = "Добавить к заметке"; /* No comment provided by engineer. */ "Choose for append" = "Выбрать заметку для добавления"; /* No comment provided by engineer. */ "New note" = "Сохранить"; /* No comment provided by engineer. */ "Project" = "Добавить в проект"; ================================================ FILE: FSNotes iOS Share/ru.lproj/MainInterface.strings ================================================ ================================================ FILE: FSNotes iOS Share/uk.lproj/InfoPlist.strings ================================================ ================================================ FILE: FSNotes iOS Share/uk.lproj/MainInterface.strings ================================================ ================================================ FILE: FSNotes iOS Share/zh-Hans-CN.lproj/InfoPlist.strings ================================================ /* Bundle display name */ "CFBundleDisplayName" = "FSNotes"; /* Bundle name */ "CFBundleName" = "FSNotes iOS分享扩展"; ================================================ FILE: FSNotes iOS Share/zh-Hans-CN.lproj/Localizable.strings ================================================ /* No comment provided by engineer. */ "Append to" = "附加到"; /* No comment provided by engineer. */ "Choose for append" = "选择追加"; /* No comment provided by engineer. */ "New note" = "新笔记"; /* No comment provided by engineer. */ "Project" = "项目"; ================================================ FILE: FSNotes iOS Share/zh-Hans.lproj/MainInterface.strings ================================================ ================================================ FILE: FSNotes.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 60; objects = { /* Begin PBXBuildFile section */ 1102DDB12EE4C280005029A6 /* EditTextView+Complete.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1102DDB02EE4C277005029A6 /* EditTextView+Complete.swift */; }; 1102DDB22EE4C280005029A6 /* EditTextView+Complete.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1102DDB02EE4C277005029A6 /* EditTextView+Complete.swift */; }; 110BE0112EE86B4A00C5E456 /* Clojure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0102EE86B4600C5E456 /* Clojure.swift */; }; 110BE0122EE86B4A00C5E456 /* Clojure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0102EE86B4600C5E456 /* Clojure.swift */; }; 110BE0132EE86B4A00C5E456 /* Clojure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0102EE86B4600C5E456 /* Clojure.swift */; }; 110BE0152EE8C1C900C5E456 /* Html.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0142EE8C1C600C5E456 /* Html.swift */; }; 110BE0162EE8C1C900C5E456 /* Html.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0142EE8C1C600C5E456 /* Html.swift */; }; 110BE0172EE8C1C900C5E456 /* Html.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0142EE8C1C600C5E456 /* Html.swift */; }; 110BE0192EE8C25100C5E456 /* Css.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0182EE8C24B00C5E456 /* Css.swift */; }; 110BE01A2EE8C25100C5E456 /* Css.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0182EE8C24B00C5E456 /* Css.swift */; }; 110BE01B2EE8C25100C5E456 /* Css.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0182EE8C24B00C5E456 /* Css.swift */; }; 110BE01D2EE8C3BE00C5E456 /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE01C2EE8C3BA00C5E456 /* Shell.swift */; }; 110BE01E2EE8C3BE00C5E456 /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE01C2EE8C3BA00C5E456 /* Shell.swift */; }; 110BE01F2EE8C3BE00C5E456 /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE01C2EE8C3BA00C5E456 /* Shell.swift */; }; 110BE0212EE8C53C00C5E456 /* TypeScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0202EE8C53600C5E456 /* TypeScript.swift */; }; 110BE0222EE8C53C00C5E456 /* TypeScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0202EE8C53600C5E456 /* TypeScript.swift */; }; 110BE0232EE8C53C00C5E456 /* TypeScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0202EE8C53600C5E456 /* TypeScript.swift */; }; 110BE0252EE8C5B000C5E456 /* Lisp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0242EE8C5AC00C5E456 /* Lisp.swift */; }; 110BE0262EE8C5B000C5E456 /* Lisp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0242EE8C5AC00C5E456 /* Lisp.swift */; }; 110BE0272EE8C5B000C5E456 /* Lisp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110BE0242EE8C5AC00C5E456 /* Lisp.swift */; }; 110D09832E9C152B001555FA /* NSRange+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110D09812E9C1525001555FA /* NSRange+.swift */; }; 110D09842E9C152B001555FA /* NSRange+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110D09812E9C1525001555FA /* NSRange+.swift */; }; 110D09852E9C152B001555FA /* NSRange+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110D09812E9C1525001555FA /* NSRange+.swift */; }; 110D09862E9C152B001555FA /* NSRange+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110D09812E9C1525001555FA /* NSRange+.swift */; }; 110E409F2EA0150300C62F49 /* NSTextCheckingResult+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110E409D2EA0150000C62F49 /* NSTextCheckingResult+.swift */; }; 110E40A02EA0150300C62F49 /* NSTextCheckingResult+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110E409D2EA0150000C62F49 /* NSTextCheckingResult+.swift */; }; 110E40A12EA0150300C62F49 /* NSTextCheckingResult+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110E409D2EA0150000C62F49 /* NSTextCheckingResult+.swift */; }; 110E40A22EA0150300C62F49 /* NSTextCheckingResult+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110E409D2EA0150000C62F49 /* NSTextCheckingResult+.swift */; }; 110E40A42EA039CE00C62F49 /* EditTextView+DragOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110E40A32EA039AA00C62F49 /* EditTextView+DragOperation.swift */; }; 110E40A52EA039CE00C62F49 /* EditTextView+DragOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110E40A32EA039AA00C62F49 /* EditTextView+DragOperation.swift */; }; 111013152EC8F1B600B6CF1B /* ImagePreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 111013142EC8F1B600B6CF1B /* ImagePreviewViewController.swift */; }; 111013182ECA102200B6CF1B /* Pasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D796EB40251E127300CE5C80 /* Pasteboard.swift */; }; 113685522EC795B80033767F /* Data+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77AD7FB27F9D1C90077BD45 /* Data+.swift */; }; 113685532EC796EF0033767F /* Note.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79FE8A01F77D04A00113CFD /* Note.swift */; }; 1136855A2EC7A21F0033767F /* NSMutableAttributedString+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113685592EC7A2130033767F /* NSMutableAttributedString+.swift */; }; 1136855C2EC7A6A50033767F /* Platform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1136855B2EC7A69C0033767F /* Platform.swift */; }; 1136855E2EC7A6A50033767F /* Platform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1136855B2EC7A69C0033767F /* Platform.swift */; }; 1136855F2EC7A6A50033767F /* Platform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1136855B2EC7A69C0033767F /* Platform.swift */; }; 113685602EC7A6A50033767F /* Platform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1136855B2EC7A69C0033767F /* Platform.swift */; }; 113685622EC869950033767F /* URL+Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113685612EC869860033767F /* URL+Image.swift */; }; 113685632EC869950033767F /* URL+Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113685612EC869860033767F /* URL+Image.swift */; }; 113685642EC869950033767F /* URL+Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113685612EC869860033767F /* URL+Image.swift */; }; 113685652EC869950033767F /* URL+Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113685612EC869860033767F /* URL+Image.swift */; }; 113685682EC889E20033767F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113685672EC889DC0033767F /* SceneDelegate.swift */; }; 1136856A2EC8AE2F0033767F /* UIBarButtonItem+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113685692EC8AE260033767F /* UIBarButtonItem+.swift */; }; 113A31A02EEE2D47009B50B0 /* Note+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113A319F2EEE2D3A009B50B0 /* Note+Preview.swift */; }; 113A31A12EEE2D47009B50B0 /* Note+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113A319F2EEE2D3A009B50B0 /* Note+Preview.swift */; }; 113A31A22EEE2D47009B50B0 /* Note+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113A319F2EEE2D3A009B50B0 /* Note+Preview.swift */; }; 113A31A32EEE2D47009B50B0 /* Note+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113A319F2EEE2D3A009B50B0 /* Note+Preview.swift */; }; 113EEBD72EDBA63D00A94F29 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D7DB5ED520248D5500E7E1B6 /* Assets.xcassets */; }; 11598DA32EDCB8D40036E387 /* UIFont+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11598DA22EDCB8D40036E387 /* UIFont+.swift */; }; 11598DA42EDCB9B40036E387 /* UIFont+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11598DA22EDCB8D40036E387 /* UIFont+.swift */; }; 1161828D2E637E31005B5EE0 /* SwiftHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 116182862E62B046005B5EE0 /* SwiftHighlighter.swift */; }; 1161828E2E637E31005B5EE0 /* SwiftHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 116182862E62B046005B5EE0 /* SwiftHighlighter.swift */; }; 1161828F2E637E31005B5EE0 /* SwiftHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 116182862E62B046005B5EE0 /* SwiftHighlighter.swift */; }; 1166D1F72E91BA8800B061CA /* CodeBlockDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1166D1F62E91BA7F00B061CA /* CodeBlockDetector.swift */; }; 1166D1F82E91BA8800B061CA /* CodeBlockDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1166D1F62E91BA7F00B061CA /* CodeBlockDetector.swift */; }; 1166D1F92E91BA8800B061CA /* CodeBlockDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1166D1F62E91BA7F00B061CA /* CodeBlockDetector.swift */; }; 1175E0922EDC929400B92794 /* ny-2026.icon in Resources */ = {isa = PBXBuildFile; fileRef = 1175E0912EDC929400B92794 /* ny-2026.icon */; }; 117C0E4D2EEDB9C30086419C /* EditTextView+Clicked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 117C0E4C2EEDB9B70086419C /* EditTextView+Clicked.swift */; }; 117C0E4E2EEDB9C40086419C /* EditTextView+Clicked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 117C0E4C2EEDB9B70086419C /* EditTextView+Clicked.swift */; }; 11A6A8FA2EF06B9D005D000A /* EditTextView+MoveLines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11A6A8F92EF06B90005D000A /* EditTextView+MoveLines.swift */; }; 11A6A8FB2EF06B9D005D000A /* EditTextView+MoveLines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11A6A8F92EF06B90005D000A /* EditTextView+MoveLines.swift */; }; 11A6A8FD2EF074D8005D000A /* EditTextView+Todo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11A6A8FC2EF074D2005D000A /* EditTextView+Todo.swift */; }; 11A6A8FE2EF074D8005D000A /* EditTextView+Todo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11A6A8FC2EF074D2005D000A /* EditTextView+Todo.swift */; }; 11A95B622EDC56DC0081ED29 /* modern.icon in Resources */ = {isa = PBXBuildFile; fileRef = 11A95B612EDC56DC0081ED29 /* modern.icon */; }; 11AA4B1F2EF9A47A0075A9E4 /* EditorViewController+ScrollPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11AA4B1E2EF9A4680075A9E4 /* EditorViewController+ScrollPosition.swift */; }; 11AA4B202EF9A47A0075A9E4 /* EditorViewController+ScrollPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11AA4B1E2EF9A4680075A9E4 /* EditorViewController+ScrollPosition.swift */; }; 11ABE5E22EEEFD0E00E7C9EB /* NotesCounterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11ABE5E12EEEFCF700E7C9EB /* NotesCounterView.swift */; }; 11ABE5E32EEEFD0E00E7C9EB /* NotesCounterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11ABE5E12EEEFCF700E7C9EB /* NotesCounterView.swift */; }; 11AF633E2E898435004E7157 /* Sql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11AF633D2E898430004E7157 /* Sql.swift */; }; 11AF633F2E898435004E7157 /* Sql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11AF633D2E898430004E7157 /* Sql.swift */; }; 11AF63402E898435004E7157 /* Sql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11AF633D2E898430004E7157 /* Sql.swift */; }; 11B3F5962F182E5900A3531D /* EditorViewController+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B3F5952F182E4E00A3531D /* EditorViewController+Search.swift */; }; 11BD71662EDC87B700541BF9 /* classic-2025.icon in Resources */ = {isa = PBXBuildFile; fileRef = 11BD71652EDC87B700541BF9 /* classic-2025.icon */; }; 11BD8F922EDDEC3D000673A7 /* GitHubDark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8F912EDDEC33000673A7 /* GitHubDark.swift */; }; 11BD8F932EDDEC3D000673A7 /* GitHubDark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8F912EDDEC33000673A7 /* GitHubDark.swift */; }; 11BD8F942EDDEC3D000673A7 /* GitHubDark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8F912EDDEC33000673A7 /* GitHubDark.swift */; }; 11BD8F962EDDF32E000673A7 /* SolarizedLight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8F952EDDF320000673A7 /* SolarizedLight.swift */; }; 11BD8F972EDDF32E000673A7 /* SolarizedLight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8F952EDDF320000673A7 /* SolarizedLight.swift */; }; 11BD8F982EDDF32E000673A7 /* SolarizedLight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8F952EDDF320000673A7 /* SolarizedLight.swift */; }; 11BD8F9A2EDDF336000673A7 /* SolarizedDark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8F992EDDF332000673A7 /* SolarizedDark.swift */; }; 11BD8F9B2EDDF336000673A7 /* SolarizedDark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8F992EDDF332000673A7 /* SolarizedDark.swift */; }; 11BD8F9C2EDDF336000673A7 /* SolarizedDark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8F992EDDF332000673A7 /* SolarizedDark.swift */; }; 11BD8F9E2EDE0235000673A7 /* AtomOneLight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8F9D2EDE022C000673A7 /* AtomOneLight.swift */; }; 11BD8F9F2EDE0235000673A7 /* AtomOneLight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8F9D2EDE022C000673A7 /* AtomOneLight.swift */; }; 11BD8FA02EDE0235000673A7 /* AtomOneLight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8F9D2EDE022C000673A7 /* AtomOneLight.swift */; }; 11BD8FA22EDE024D000673A7 /* AtomOneDark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8FA12EDE023E000673A7 /* AtomOneDark.swift */; }; 11BD8FA32EDE024D000673A7 /* AtomOneDark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8FA12EDE023E000673A7 /* AtomOneDark.swift */; }; 11BD8FA42EDE024D000673A7 /* AtomOneDark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8FA12EDE023E000673A7 /* AtomOneDark.swift */; }; 11BD8FA62EDE0683000673A7 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8FA52EDE0679000673A7 /* Theme.swift */; }; 11BD8FA72EDE0683000673A7 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8FA52EDE0679000673A7 /* Theme.swift */; }; 11BD8FA82EDE0683000673A7 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BD8FA52EDE0679000673A7 /* Theme.swift */; }; 11BD8FAA2EDE1AF8000673A7 /* UserDataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB3820FEDE230005E120 /* UserDataService.swift */; }; 11BD8FAB2EDE1AF8000673A7 /* UserDataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB3820FEDE230005E120 /* UserDataService.swift */; }; 11BF066A2EE331B5006C7336 /* Scala.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF06692EE331B2006C7336 /* Scala.swift */; }; 11BF066B2EE331B5006C7336 /* Scala.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF06692EE331B2006C7336 /* Scala.swift */; }; 11BF066C2EE331B5006C7336 /* Scala.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF06692EE331B2006C7336 /* Scala.swift */; }; 11BF066E2EE33201006C7336 /* Bash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF066D2EE331FE006C7336 /* Bash.swift */; }; 11BF066F2EE33201006C7336 /* Bash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF066D2EE331FE006C7336 /* Bash.swift */; }; 11BF06702EE33201006C7336 /* Bash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF066D2EE331FE006C7336 /* Bash.swift */; }; 11BF06722EE33262006C7336 /* Haskell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF06712EE3325F006C7336 /* Haskell.swift */; }; 11BF06732EE33262006C7336 /* Haskell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF06712EE3325F006C7336 /* Haskell.swift */; }; 11BF06742EE33262006C7336 /* Haskell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF06712EE3325F006C7336 /* Haskell.swift */; }; 11BF06762EE49546006C7336 /* Lua.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF06752EE49542006C7336 /* Lua.swift */; }; 11BF06772EE49546006C7336 /* Lua.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF06752EE49542006C7336 /* Lua.swift */; }; 11BF06782EE49546006C7336 /* Lua.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF06752EE49542006C7336 /* Lua.swift */; }; 11BF067A2EE495C2006C7336 /* Perl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF06792EE495C0006C7336 /* Perl.swift */; }; 11BF067B2EE495C2006C7336 /* Perl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF06792EE495C0006C7336 /* Perl.swift */; }; 11BF067C2EE495C2006C7336 /* Perl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF06792EE495C0006C7336 /* Perl.swift */; }; 11BF067E2EE4968C006C7336 /* Erlang.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF067D2EE49689006C7336 /* Erlang.swift */; }; 11BF067F2EE4968C006C7336 /* Erlang.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF067D2EE49689006C7336 /* Erlang.swift */; }; 11BF06802EE4968C006C7336 /* Erlang.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BF067D2EE49689006C7336 /* Erlang.swift */; }; 11D6C0C72EE2256B006017F0 /* Python.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0C62EE22567006017F0 /* Python.swift */; }; 11D6C0C82EE2256B006017F0 /* Python.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0C62EE22567006017F0 /* Python.swift */; }; 11D6C0C92EE2256B006017F0 /* Python.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0C62EE22567006017F0 /* Python.swift */; }; 11D6C0CB2EE225E7006017F0 /* C.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0CA2EE225E1006017F0 /* C.swift */; }; 11D6C0CC2EE225E7006017F0 /* C.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0CA2EE225E1006017F0 /* C.swift */; }; 11D6C0CD2EE225E7006017F0 /* C.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0CA2EE225E1006017F0 /* C.swift */; }; 11D6C0CF2EE2279E006017F0 /* Cpp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0CE2EE22799006017F0 /* Cpp.swift */; }; 11D6C0D02EE2279E006017F0 /* Cpp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0CE2EE22799006017F0 /* Cpp.swift */; }; 11D6C0D12EE2279E006017F0 /* Cpp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0CE2EE22799006017F0 /* Cpp.swift */; }; 11D6C0D32EE227F9006017F0 /* Java.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0D22EE227F6006017F0 /* Java.swift */; }; 11D6C0D42EE227F9006017F0 /* Java.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0D22EE227F6006017F0 /* Java.swift */; }; 11D6C0D52EE227F9006017F0 /* Java.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0D22EE227F6006017F0 /* Java.swift */; }; 11D6C0D72EE229E9006017F0 /* Go.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0D62EE229E7006017F0 /* Go.swift */; }; 11D6C0D82EE229E9006017F0 /* Go.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0D62EE229E7006017F0 /* Go.swift */; }; 11D6C0D92EE229E9006017F0 /* Go.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0D62EE229E7006017F0 /* Go.swift */; }; 11D6C0DB2EE22B0F006017F0 /* Rust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0DA2EE22B0D006017F0 /* Rust.swift */; }; 11D6C0DC2EE22B0F006017F0 /* Rust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0DA2EE22B0D006017F0 /* Rust.swift */; }; 11D6C0DD2EE22B0F006017F0 /* Rust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0DA2EE22B0D006017F0 /* Rust.swift */; }; 11D6C0DF2EE22B77006017F0 /* Csharp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0DE2EE22B74006017F0 /* Csharp.swift */; }; 11D6C0E02EE22B77006017F0 /* Csharp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0DE2EE22B74006017F0 /* Csharp.swift */; }; 11D6C0E12EE22B77006017F0 /* Csharp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0DE2EE22B74006017F0 /* Csharp.swift */; }; 11D6C0E32EE22BF2006017F0 /* Kotlin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0E22EE22BEE006017F0 /* Kotlin.swift */; }; 11D6C0E42EE22BF2006017F0 /* Kotlin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0E22EE22BEE006017F0 /* Kotlin.swift */; }; 11D6C0E52EE22BF2006017F0 /* Kotlin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0E22EE22BEE006017F0 /* Kotlin.swift */; }; 11D6C0E72EE22C11006017F0 /* R.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0E62EE22C0F006017F0 /* R.swift */; }; 11D6C0E82EE22C11006017F0 /* R.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0E62EE22C0F006017F0 /* R.swift */; }; 11D6C0E92EE22C11006017F0 /* R.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0E62EE22C0F006017F0 /* R.swift */; }; 11D6C0EB2EE22C6B006017F0 /* Ruby.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0EA2EE22C69006017F0 /* Ruby.swift */; }; 11D6C0EC2EE22C6B006017F0 /* Ruby.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0EA2EE22C69006017F0 /* Ruby.swift */; }; 11D6C0ED2EE22C6B006017F0 /* Ruby.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0EA2EE22C69006017F0 /* Ruby.swift */; }; 11D6C0EF2EE22CBC006017F0 /* Matlab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0EE2EE22CB8006017F0 /* Matlab.swift */; }; 11D6C0F02EE22CBC006017F0 /* Matlab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0EE2EE22CB8006017F0 /* Matlab.swift */; }; 11D6C0F12EE22CBC006017F0 /* Matlab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0EE2EE22CB8006017F0 /* Matlab.swift */; }; 11D6C0F32EE22D3B006017F0 /* Dart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0F22EE22D39006017F0 /* Dart.swift */; }; 11D6C0F42EE22D3B006017F0 /* Dart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0F22EE22D39006017F0 /* Dart.swift */; }; 11D6C0F52EE22D3B006017F0 /* Dart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0F22EE22D39006017F0 /* Dart.swift */; }; 11D6C0F72EE22D78006017F0 /* Vb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0F62EE22D73006017F0 /* Vb.swift */; }; 11D6C0F82EE22D78006017F0 /* Vb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0F62EE22D73006017F0 /* Vb.swift */; }; 11D6C0F92EE22D78006017F0 /* Vb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0F62EE22D73006017F0 /* Vb.swift */; }; 11D6C0FB2EE22E00006017F0 /* Assembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0FA2EE22DFC006017F0 /* Assembly.swift */; }; 11D6C0FC2EE22E00006017F0 /* Assembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0FA2EE22DFC006017F0 /* Assembly.swift */; }; 11D6C0FD2EE22E00006017F0 /* Assembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0FA2EE22DFC006017F0 /* Assembly.swift */; }; 11D6C0FF2EE22E4A006017F0 /* Scratch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0FE2EE22E48006017F0 /* Scratch.swift */; }; 11D6C1002EE22E4A006017F0 /* Scratch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0FE2EE22E48006017F0 /* Scratch.swift */; }; 11D6C1012EE22E4A006017F0 /* Scratch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C0FE2EE22E48006017F0 /* Scratch.swift */; }; 11D6C1032EE22E8F006017F0 /* Groovy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C1022EE22E8B006017F0 /* Groovy.swift */; }; 11D6C1042EE22E8F006017F0 /* Groovy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C1022EE22E8B006017F0 /* Groovy.swift */; }; 11D6C1052EE22E8F006017F0 /* Groovy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C1022EE22E8B006017F0 /* Groovy.swift */; }; 11D6C1072EE22EED006017F0 /* ObjectiveC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C1062EE22EE8006017F0 /* ObjectiveC.swift */; }; 11D6C1082EE22EED006017F0 /* ObjectiveC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C1062EE22EE8006017F0 /* ObjectiveC.swift */; }; 11D6C1092EE22EED006017F0 /* ObjectiveC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D6C1062EE22EE8006017F0 /* ObjectiveC.swift */; }; 11D702A62E5ADDED004DBAEC /* LayoutManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D702A52E5ADDE2004DBAEC /* LayoutManager.swift */; }; 11D702A72E5ADDED004DBAEC /* LayoutManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D702A52E5ADDE2004DBAEC /* LayoutManager.swift */; }; 11D702AC2E5B8E0C004DBAEC /* HtmlExtractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D702AB2E5B8E02004DBAEC /* HtmlExtractor.swift */; }; 11D702AD2E5B8E0C004DBAEC /* HtmlExtractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D702AB2E5B8E02004DBAEC /* HtmlExtractor.swift */; }; 11D702AE2E5B8E0C004DBAEC /* HtmlExtractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D702AB2E5B8E02004DBAEC /* HtmlExtractor.swift */; }; 11D9431A2E643EF40010CC2B /* JavaScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D943192E643EF20010CC2B /* JavaScript.swift */; }; 11D9431B2E643EF40010CC2B /* JavaScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D943192E643EF20010CC2B /* JavaScript.swift */; }; 11D9431C2E643EF40010CC2B /* JavaScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D943192E643EF20010CC2B /* JavaScript.swift */; }; 11D9431E2E643F250010CC2B /* Swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D9431D2E643F1F0010CC2B /* Swift.swift */; }; 11D9431F2E643F250010CC2B /* Swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D9431D2E643F1F0010CC2B /* Swift.swift */; }; 11D943202E643F250010CC2B /* Swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D9431D2E643F1F0010CC2B /* Swift.swift */; }; 11D943222E643F410010CC2B /* Php.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D943212E643F3B0010CC2B /* Php.swift */; }; 11D943232E643F410010CC2B /* Php.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D943212E643F3B0010CC2B /* Php.swift */; }; 11D943242E643F410010CC2B /* Php.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D943212E643F3B0010CC2B /* Php.swift */; }; 11D943272E643F630010CC2B /* GitHubLight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D943262E643F5E0010CC2B /* GitHubLight.swift */; }; 11D943282E643F630010CC2B /* GitHubLight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D943262E643F5E0010CC2B /* GitHubLight.swift */; }; 11D943292E643F630010CC2B /* GitHubLight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D943262E643F5E0010CC2B /* GitHubLight.swift */; }; 11F018AC2EF7E78600F07580 /* MPreviewFindPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11F018AB2EF7E77B00F07580 /* MPreviewFindPanel.swift */; }; 11F018AD2EF7E78600F07580 /* MPreviewFindPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11F018AB2EF7E77B00F07580 /* MPreviewFindPanel.swift */; }; 11F018B22EF8415600F07580 /* MPreviewContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11F018B12EF8415200F07580 /* MPreviewContainerView.swift */; }; 11F018B32EF8415600F07580 /* MPreviewContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11F018B12EF8415200F07580 /* MPreviewContainerView.swift */; }; 11F1771A2EF1E93500CC566F /* ViewController+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11F177192EF1E92C00CC566F /* ViewController+Menu.swift */; }; 11F1771B2EF1E93500CC566F /* ViewController+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11F177192EF1E92C00CC566F /* ViewController+Menu.swift */; }; 11F2D4F42F104322002E4E47 /* Project+Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11F2D4F32F1042F4002E4E47 /* Project+Date.swift */; }; 11F2D4F52F104322002E4E47 /* Project+Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11F2D4F32F1042F4002E4E47 /* Project+Date.swift */; }; 11F2D4F62F104322002E4E47 /* Project+Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11F2D4F32F1042F4002E4E47 /* Project+Date.swift */; }; 11F2D4F72F104322002E4E47 /* Project+Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11F2D4F32F1042F4002E4E47 /* Project+Date.swift */; }; 11F389562EEA10930008EC18 /* Mermaid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11F389552EEA108C0008EC18 /* Mermaid.swift */; }; 11F389572EEA10930008EC18 /* Mermaid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11F389552EEA108C0008EC18 /* Mermaid.swift */; }; 11F389582EEA10930008EC18 /* Mermaid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11F389552EEA108C0008EC18 /* Mermaid.swift */; }; 11F3F4832EDB0E0B00435CBF /* URL+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB2A20FEDE230005E120 /* URL+.swift */; }; 11F3F4842EDB0E0B00435CBF /* URL+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB2A20FEDE230005E120 /* URL+.swift */; }; 11F3F4852EDB0E2A00435CBF /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB2920FEDE230005E120 /* String+.swift */; }; 11F3F4862EDB0E2A00435CBF /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB2920FEDE230005E120 /* String+.swift */; }; 11F3F4872EDB0E4400435CBF /* DateFormatter+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB2820FEDE230005E120 /* DateFormatter+.swift */; }; 11F3F4882EDB0E4400435CBF /* DateFormatter+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB2820FEDE230005E120 /* DateFormatter+.swift */; }; 11F43F3C2F127EE300652350 /* Meet FSNotes 7.textbundle in Resources */ = {isa = PBXBuildFile; fileRef = 11F43F3A2F127C6900652350 /* Meet FSNotes 7.textbundle */; }; 11F5D53B2EFDA17000A66466 /* modern.icon in Resources */ = {isa = PBXBuildFile; fileRef = 11F5D53A2EFDA17000A66466 /* modern.icon */; }; 11F5D53C2EFDA17000A66466 /* modern.icon in Resources */ = {isa = PBXBuildFile; fileRef = 11F5D53A2EFDA17000A66466 /* modern.icon */; }; 275592971F3AE9B5006B8988 /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275592961F3AE9B5006B8988 /* MainWindowController.swift */; }; 2799407C218484C900727B20 /* TitleBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2799407B218484C900727B20 /* TitleBarView.swift */; }; 42E001C62ADAC2930099E7AD /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 42E001C52ADAC2930099E7AD /* Localizable.xcstrings */; }; 42E001C72ADAC2930099E7AD /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 42E001C52ADAC2930099E7AD /* Localizable.xcstrings */; }; 42E001CA2ADAC2930099E7AD /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 42E001C92ADAC2930099E7AD /* Localizable.xcstrings */; }; 42E001CC2ADAC2930099E7AD /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 42E001CB2ADAC2930099E7AD /* InfoPlist.xcstrings */; }; 42E001CF2ADAC2930099E7AD /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 42E001CE2ADAC2930099E7AD /* Localizable.xcstrings */; }; 48BEA1E16CCED6900AD756F7 /* Pods_FSNotes_iOS_Share_Extension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 99068B82274CF88F23C4761D /* Pods_FSNotes_iOS_Share_Extension.framework */; }; 8F7136EE23490CBF004DFA6E /* Markdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7136ED23490CBF004DFA6E /* Markdown.swift */; }; 8F7136F023490CBF004DFA6E /* Markdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7136ED23490CBF004DFA6E /* Markdown.swift */; }; BE957A4A1B908EC91BECB3D3 /* Pods_FSNotes__iCloud_.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6066605B9BAF43A4BF3B60C1 /* Pods_FSNotes__iCloud_.framework */; }; CE3427A778205E1713A014B9 /* Pods_FSNotes_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 85B72F46887638CA9CC70D39 /* Pods_FSNotes_iOS.framework */; }; D4DB932C9F51CAE71393A28B /* Pods_FSNotes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F616385FF783029192B97DF6 /* Pods_FSNotes.framework */; }; D7013E0026C3B116006F58E3 /* NSColor+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7013DFF26C3B116006F58E3 /* NSColor+.swift */; }; D7013E0126C3B116006F58E3 /* NSColor+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7013DFF26C3B116006F58E3 /* NSColor+.swift */; }; D7038E2620FB24E000A54E69 /* NoteAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7038E2520FB24E000A54E69 /* NoteAttachment.swift */; }; D7038E2720FB24E000A54E69 /* NoteAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7038E2520FB24E000A54E69 /* NoteAttachment.swift */; }; D706396A202230BB00BC8446 /* EditorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7063969202230BB00BC8446 /* EditorViewController.swift */; }; D70716DC2307E82900B44B0D /* SingleImageTouchDownGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D70716DB2307E82900B44B0D /* SingleImageTouchDownGestureRecognizer.swift */; }; D708AC672000EF5800A1760F /* NoteType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D708AC662000EF5800A1760F /* NoteType.swift */; }; D708AC682000F0E100A1760F /* NoteType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D708AC662000EF5800A1760F /* NoteType.swift */; }; D709C9E229AFD9E0006EF9A8 /* GitTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D709C9E129AFD9E0006EF9A8 /* GitTableViewCell.swift */; }; D70B1FAB29213EDF003923DC /* HyperlinkTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D70B1FAA29213EDF003923DC /* HyperlinkTextField.swift */; }; D70B1FAC29213EE0003923DC /* HyperlinkTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D70B1FAA29213EDF003923DC /* HyperlinkTextField.swift */; }; D70E9DEE2901AE9100A3C634 /* Branch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5628EB5EBE008B3BBC /* Branch.swift */; }; D70E9DEF2901AE9400A3C634 /* Branches.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5728EB5EBE008B3BBC /* Branches.swift */; }; D70E9DF02901AE9700A3C634 /* BranchesIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5828EB5EBE008B3BBC /* BranchesIterator.swift */; }; D70E9DF12901AE9B00A3C634 /* KeyAuthentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6E28EB5EC0008B3BBC /* KeyAuthentication.swift */; }; D70E9DF22901AE9E00A3C634 /* Authentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6F28EB5EC0008B3BBC /* Authentication.swift */; }; D70E9DF32901AEA100A3C634 /* PasswordAuthentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7028EB5EC0008B3BBC /* PasswordAuthentication.swift */; }; D70E9DF42901AEA800A3C634 /* Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5428EB5EBE008B3BBC /* Commit.swift */; }; D70E9DF52901AEAF00A3C634 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5F28EB5EBE008B3BBC /* Blob.swift */; }; D70E9DF62901AEAF00A3C634 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5C28EB5EBE008B3BBC /* Error.swift */; }; D70E9DF72901AEAF00A3C634 /* ConfigManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5B28EB5EBE008B3BBC /* ConfigManager.swift */; }; D70E9DF82901AEAF00A3C634 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5A28EB5EBE008B3BBC /* Strings.swift */; }; D70E9DF92901AEAF00A3C634 /* Progress.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5E28EB5EBE008B3BBC /* Progress.swift */; }; D70E9DFA2901AEAF00A3C634 /* Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6128EB5EBF008B3BBC /* Signature.swift */; }; D70E9DFB2901AEAF00A3C634 /* Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6228EB5EBF008B3BBC /* Object.swift */; }; D70E9DFC2901AEAF00A3C634 /* Wrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5D28EB5EBE008B3BBC /* Wrapper.swift */; }; D70E9DFD2901AEAF00A3C634 /* OID.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6028EB5EBF008B3BBC /* OID.swift */; }; D70E9DFE2901AEAF00A3C634 /* StaticSshKeyDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E32C2B28F8D0740048614B /* StaticSshKeyDelegate.swift */; }; D70E9DFF2901AEAF00A3C634 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = D771E96E28EDFBF600CD4871 /* Errors.swift */; }; D70E9E002901AEB600A3C634 /* Diff.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6C28EB5EC0008B3BBC /* Diff.swift */; }; D70E9E012901AEB600A3C634 /* DiffEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6B28EB5EC0008B3BBC /* DiffEntry.swift */; }; D70E9E022901AEBF00A3C634 /* Head.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7E28EB5EC2008B3BBC /* Head.swift */; }; D70E9E032901AEBF00A3C634 /* Head+Checkout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7D28EB5EC2008B3BBC /* Head+Checkout.swift */; }; D70E9E042901AEBF00A3C634 /* Head+Merge.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7F28EB5EC2008B3BBC /* Head+Merge.swift */; }; D70E9E052901AEC400A3C634 /* Index+Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7B28EB5EC1008B3BBC /* Index+Files.swift */; }; D70E9E062901AEC400A3C634 /* Index.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7928EB5EC1008B3BBC /* Index.swift */; }; D70E9E072901AEC400A3C634 /* Index+Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7A28EB5EC1008B3BBC /* Index+Commit.swift */; }; D70E9E082901AEC900A3C634 /* Reference.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8A28EB5EC3008B3BBC /* Reference.swift */; }; D70E9E0A2901AECF00A3C634 /* Remote.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7228EB5EC0008B3BBC /* Remote.swift */; }; D70E9E0B2901AED400A3C634 /* Remotes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7328EB5EC0008B3BBC /* Remotes.swift */; }; D70E9E0C2901AEDA00A3C634 /* Repository+Lookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8728EB5EC3008B3BBC /* Repository+Lookup.swift */; }; D70E9E0D2901AEDA00A3C634 /* Repository+Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8828EB5EC3008B3BBC /* Repository+Commit.swift */; }; D70E9E0E2901AEDA00A3C634 /* Repository+Open.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8628EB5EC3008B3BBC /* Repository+Open.swift */; }; D70E9E0F2901AEDA00A3C634 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8528EB5EC3008B3BBC /* Repository.swift */; }; D70E9E102901AEDA00A3C634 /* RepositoryManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8428EB5EC3008B3BBC /* RepositoryManager.swift */; }; D70E9E112901AEE100A3C634 /* RevisionIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6828EB5EBF008B3BBC /* RevisionIterator.swift */; }; D70E9E122901AEE100A3C634 /* FileHistoryIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6928EB5EBF008B3BBC /* FileHistoryIterator.swift */; }; D70E9E132901AEE600A3C634 /* Statuses.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7528EB5EC1008B3BBC /* Statuses.swift */; }; D70E9E142901AEEB00A3C634 /* StatusIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7728EB5EC1008B3BBC /* StatusIterator.swift */; }; D70E9E152901AEEB00A3C634 /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7628EB5EC1008B3BBC /* Status.swift */; }; D70E9E162901AEF300A3C634 /* TagIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6428EB5EBF008B3BBC /* TagIterator.swift */; }; D70E9E172901AEF300A3C634 /* Tags.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6528EB5EBF008B3BBC /* Tags.swift */; }; D70E9E182901AEF300A3C634 /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6628EB5EBF008B3BBC /* Tag.swift */; }; D70E9E192901AEF900A3C634 /* TreeEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8128EB5EC2008B3BBC /* TreeEntry.swift */; }; D70E9E1A2901AEF900A3C634 /* Tree.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8228EB5EC2008B3BBC /* Tree.swift */; }; D70F830428CE858E004818C5 /* Git in Frameworks */ = {isa = PBXBuildFile; productRef = D70F830328CE858E004818C5 /* Git */; }; D70F830628CE8596004818C5 /* Git in Frameworks */ = {isa = PBXBuildFile; productRef = D70F830528CE8596004818C5 /* Git */; }; D7104A64230BD8C500B6D8EE /* SortDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7104A63230BD8C500B6D8EE /* SortDirection.swift */; }; D7104A65230BD8C500B6D8EE /* SortDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7104A63230BD8C500B6D8EE /* SortDirection.swift */; }; D7104A67230BD8C500B6D8EE /* SortDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7104A63230BD8C500B6D8EE /* SortDirection.swift */; }; D7104A68230BD8C500B6D8EE /* SortDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7104A63230BD8C500B6D8EE /* SortDirection.swift */; }; D71354042042AFC800E3776F /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71354032042AFC800E3776F /* SettingsViewController.swift */; }; D714496220C72D3500D7AD46 /* UIImage+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D714496120C72D3400D7AD46 /* UIImage+.swift */; }; D714749B279CE8EE001A8B29 /* MainNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D714749A279CE8EE001A8B29 /* MainNavigationController.swift */; }; D714749D279D7DBC001A8B29 /* SearchQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = D714749C279D7DBC001A8B29 /* SearchQuery.swift */; }; D7153DFD2285A93300A2C20F /* AboutWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7153DFC2285A93300A2C20F /* AboutWindowController.swift */; }; D7153DFE2285A93300A2C20F /* AboutWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7153DFC2285A93300A2C20F /* AboutWindowController.swift */; }; D7153E052285C09C00A2C20F /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7153E042285C09C00A2C20F /* AboutViewController.swift */; }; D7153E062285C09C00A2C20F /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7153E042285C09C00A2C20F /* AboutViewController.swift */; }; D7153E092285EC6100A2C20F /* TitleTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7153E082285EC6100A2C20F /* TitleTextField.swift */; }; D7153E0A2285EC6100A2C20F /* TitleTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7153E082285EC6100A2C20F /* TitleTextField.swift */; }; D7163D2F24E81B5C00B1FC05 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D7163D2E24E81B5C00B1FC05 /* Main.storyboard */; }; D7163D3424E81D9900B1FC05 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D7163D3324E81D9900B1FC05 /* MainInterface.storyboard */; }; D7166F541F32F75E001A883F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D7793C781F211C6000CA39B7 /* Main.storyboard */; }; D7170C1D20F8565B001DDB36 /* FileSystemEventManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7170C1C20F8565B001DDB36 /* FileSystemEventManager.swift */; }; D7170C1E20F8565B001DDB36 /* FileSystemEventManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7170C1C20F8565B001DDB36 /* FileSystemEventManager.swift */; }; D71760932826CAC4009794D8 /* MPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730829123084340003185D1 /* MPreviewView.swift */; }; D7194B872023863A0062F1E3 /* ImagesProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D777D7802009115C00D86B33 /* ImagesProcessor.swift */; }; D71AA0222143A4A8004AFD2A /* MoveViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71AA0212143A4A8004AFD2A /* MoveViewController.swift */; }; D71B9D7A2867027000D2F323 /* NoteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71B9D792867027000D2F323 /* NoteViewController.swift */; }; D71B9D7B2867027000D2F323 /* NoteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71B9D792867027000D2F323 /* NoteViewController.swift */; }; D71B9D822868658100D2F323 /* EditorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71B9D812868658100D2F323 /* EditorViewController.swift */; }; D71B9D832868658100D2F323 /* EditorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71B9D812868658100D2F323 /* EditorViewController.swift */; }; D71B9D862868BF7F00D2F323 /* TextStorageProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71B9D852868BF7F00D2F323 /* TextStorageProcessor.swift */; }; D71B9D872868BF7F00D2F323 /* TextStorageProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71B9D852868BF7F00D2F323 /* TextStorageProcessor.swift */; }; D71B9D892868BF7F00D2F323 /* TextStorageProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71B9D852868BF7F00D2F323 /* TextStorageProcessor.swift */; }; D71C4A4E1F520F1B00EBA30B /* MPreview.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D71C4A4D1F520F0E00EBA30B /* MPreview.bundle */; }; D71FD21F2101B2D5008BEFA1 /* NoteAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7038E2520FB24E000A54E69 /* NoteAttachment.swift */; }; D71FD2252101CFD0008BEFA1 /* UITextView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71FD2242101CFD0008BEFA1 /* UITextView+.swift */; }; D720240F22A9412B000A7691 /* Markdown.icns in Resources */ = {isa = PBXBuildFile; fileRef = D720240922A9412B000A7691 /* Markdown.icns */; }; D720241022A9412B000A7691 /* Markdown.icns in Resources */ = {isa = PBXBuildFile; fileRef = D720240922A9412B000A7691 /* Markdown.icns */; }; D720241222A9412B000A7691 /* Text.icns in Resources */ = {isa = PBXBuildFile; fileRef = D720240A22A9412B000A7691 /* Text.icns */; }; D720241322A9412B000A7691 /* Text.icns in Resources */ = {isa = PBXBuildFile; fileRef = D720240A22A9412B000A7691 /* Text.icns */; }; D720241522A9412B000A7691 /* TextBundle.icns in Resources */ = {isa = PBXBuildFile; fileRef = D720240B22A9412B000A7691 /* TextBundle.icns */; }; D720241622A9412B000A7691 /* TextBundle.icns in Resources */ = {isa = PBXBuildFile; fileRef = D720240B22A9412B000A7691 /* TextBundle.icns */; }; D720241922A941A3000A7691 /* EncryptedTextPack.icns in Resources */ = {isa = PBXBuildFile; fileRef = D720241822A941A3000A7691 /* EncryptedTextPack.icns */; }; D720241A22A941A3000A7691 /* EncryptedTextPack.icns in Resources */ = {isa = PBXBuildFile; fileRef = D720241822A941A3000A7691 /* EncryptedTextPack.icns */; }; D72682AA29BE8E2000F6E961 /* RepositoryAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72682A929BE8E1F00F6E961 /* RepositoryAction.swift */; }; D72682AB29BE8E2000F6E961 /* RepositoryAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72682A929BE8E1F00F6E961 /* RepositoryAction.swift */; }; D72682AD29BE8E2000F6E961 /* RepositoryAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72682A929BE8E1F00F6E961 /* RepositoryAction.swift */; }; D72682AE29BE8E2100F6E961 /* RepositoryAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72682A929BE8E1F00F6E961 /* RepositoryAction.swift */; }; D726DE8A287ACC1E00F8406C /* NSWindow+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D726DE89287ACC1E00F8406C /* NSWindow+.swift */; }; D726DE8B287ACC1E00F8406C /* NSWindow+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D726DE89287ACC1E00F8406C /* NSWindow+.swift */; }; D72DAF0829B27D75001243BB /* ProjectSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72DAF0729B27D75001243BB /* ProjectSettings.swift */; }; D72DAF0929B27D75001243BB /* ProjectSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72DAF0729B27D75001243BB /* ProjectSettings.swift */; }; D72DAF0B29B27D75001243BB /* ProjectSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72DAF0729B27D75001243BB /* ProjectSettings.swift */; }; D72DAF0C29B27D75001243BB /* ProjectSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72DAF0729B27D75001243BB /* ProjectSettings.swift */; }; D730829223084340003185D1 /* MPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730829123084340003185D1 /* MPreviewView.swift */; }; D730829323084340003185D1 /* MPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730829123084340003185D1 /* MPreviewView.swift */; }; D730BD27222BF30700E69C93 /* KeychainConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD26222BF30700E69C93 /* KeychainConfiguration.swift */; }; D730BD28222BF30700E69C93 /* KeychainConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD26222BF30700E69C93 /* KeychainConfiguration.swift */; }; D730BD2A222BF32A00E69C93 /* KeychainPasswordItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD29222BF32A00E69C93 /* KeychainPasswordItem.swift */; }; D730BD2B222BF32A00E69C93 /* KeychainPasswordItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD29222BF32A00E69C93 /* KeychainPasswordItem.swift */; }; D730BD2E222DABA100E69C93 /* NoteContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD2D222DABA100E69C93 /* NoteContainer.swift */; }; D730BD2F222DABA100E69C93 /* NoteContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD2D222DABA100E69C93 /* NoteContainer.swift */; }; D730BD30222DABA100E69C93 /* NoteContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD2D222DABA100E69C93 /* NoteContainer.swift */; }; D730BD31222DABA100E69C93 /* NoteContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD2D222DABA100E69C93 /* NoteContainer.swift */; }; D730BD35222DB11E00E69C93 /* TextBundleInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD34222DB11E00E69C93 /* TextBundleInfo.swift */; }; D730BD36222DB11E00E69C93 /* TextBundleInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD34222DB11E00E69C93 /* TextBundleInfo.swift */; }; D730BD37222DB11E00E69C93 /* TextBundleInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD34222DB11E00E69C93 /* TextBundleInfo.swift */; }; D730BD38222DB11E00E69C93 /* TextBundleInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD34222DB11E00E69C93 /* TextBundleInfo.swift */; }; D730BD3C222DB9FC00E69C93 /* NameHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD3B222DB9FC00E69C93 /* NameHelper.swift */; }; D730BD3D222DB9FC00E69C93 /* NameHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD3B222DB9FC00E69C93 /* NameHelper.swift */; }; D730BD3E222DB9FC00E69C93 /* NameHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD3B222DB9FC00E69C93 /* NameHelper.swift */; }; D730BD3F222DB9FC00E69C93 /* NameHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD3B222DB9FC00E69C93 /* NameHelper.swift */; }; D730BD45223510A700E69C93 /* KeychainPasswordItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD29222BF32A00E69C93 /* KeychainPasswordItem.swift */; }; D730BD46223510A900E69C93 /* KeychainConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD26222BF30700E69C93 /* KeychainConfiguration.swift */; }; D730BD5A223BFEB200E69C93 /* RuntimeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD59223BFEB200E69C93 /* RuntimeError.swift */; }; D730BD5B223BFEB200E69C93 /* RuntimeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD59223BFEB200E69C93 /* RuntimeError.swift */; }; D730BD5C223BFEB200E69C93 /* RuntimeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD59223BFEB200E69C93 /* RuntimeError.swift */; }; D7315ECF215ECF3000AB49D4 /* EditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7315ECE215ECF3000AB49D4 /* EditorView.swift */; }; D7315ED0215ECF3000AB49D4 /* EditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7315ECE215ECF3000AB49D4 /* EditorView.swift */; }; D7315ED2215ED15500AB49D4 /* SidebarSplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7315ED1215ED15500AB49D4 /* SidebarSplitView.swift */; }; D7315ED3215ED15500AB49D4 /* SidebarSplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7315ED1215ED15500AB49D4 /* SidebarSplitView.swift */; }; D7315ED5215ED95600AB49D4 /* NSAppearance+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7315ED4215ED95600AB49D4 /* NSAppearance+.swift */; }; D7315ED6215ED95600AB49D4 /* NSAppearance+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7315ED4215ED95600AB49D4 /* NSAppearance+.swift */; }; D73290BA2099F0AB0003F647 /* UndoData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78678CA2093AE10001A6620 /* UndoData.swift */; }; D735E5BD1F2EF66000173215 /* NoteCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D735E5BC1F2EF66000173215 /* NoteCellView.swift */; }; D735E5BF1F2F001500173215 /* NoteRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D735E5BE1F2F001500173215 /* NoteRowView.swift */; }; D73673A820D10CF2000BA61D /* CloudDriveManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73673A720D10CF2000BA61D /* CloudDriveManager.swift */; }; D736DDA927B5DD370012ED70 /* EditorSelectionRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = D736DDA827B5DD370012ED70 /* EditorSelectionRect.swift */; }; D736DDAB27BABFFB0012ED70 /* RevisionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D736DDAA27BABFF80012ED70 /* RevisionsViewController.swift */; }; D736DDAD27BAC7940012ED70 /* Note+History.swift in Sources */ = {isa = PBXBuildFile; fileRef = D736DDAC27BAC7940012ED70 /* Note+History.swift */; }; D73794BF2336642500E75A28 /* (null) in Sources */ = {isa = PBXBuildFile; }; D73794C123366F5200E75A28 /* ImageScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73794C023366F5200E75A28 /* ImageScrollView.swift */; }; D738356D2242871400B260DD /* MasterPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D738356C2242871400B260DD /* MasterPasswordViewController.swift */; }; D738356E2242871400B260DD /* MasterPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D738356C2242871400B260DD /* MasterPasswordViewController.swift */; }; D73B3135298FBF4400F46144 /* GitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73B3134298FBF4400F46144 /* GitViewController.swift */; }; D73BCC8C28EB5EC3008B3BBC /* Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5428EB5EBE008B3BBC /* Commit.swift */; }; D73BCC8D28EB5EC3008B3BBC /* Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5428EB5EBE008B3BBC /* Commit.swift */; }; D73BCC8F28EB5EC3008B3BBC /* Branch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5628EB5EBE008B3BBC /* Branch.swift */; }; D73BCC9028EB5EC3008B3BBC /* Branch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5628EB5EBE008B3BBC /* Branch.swift */; }; D73BCC9228EB5EC3008B3BBC /* Branches.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5728EB5EBE008B3BBC /* Branches.swift */; }; D73BCC9328EB5EC3008B3BBC /* Branches.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5728EB5EBE008B3BBC /* Branches.swift */; }; D73BCC9528EB5EC3008B3BBC /* BranchesIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5828EB5EBE008B3BBC /* BranchesIterator.swift */; }; D73BCC9628EB5EC3008B3BBC /* BranchesIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5828EB5EBE008B3BBC /* BranchesIterator.swift */; }; D73BCC9828EB5EC3008B3BBC /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5A28EB5EBE008B3BBC /* Strings.swift */; }; D73BCC9928EB5EC3008B3BBC /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5A28EB5EBE008B3BBC /* Strings.swift */; }; D73BCC9B28EB5EC3008B3BBC /* ConfigManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5B28EB5EBE008B3BBC /* ConfigManager.swift */; }; D73BCC9C28EB5EC3008B3BBC /* ConfigManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5B28EB5EBE008B3BBC /* ConfigManager.swift */; }; D73BCC9E28EB5EC3008B3BBC /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5C28EB5EBE008B3BBC /* Error.swift */; }; D73BCC9F28EB5EC3008B3BBC /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5C28EB5EBE008B3BBC /* Error.swift */; }; D73BCCA128EB5EC3008B3BBC /* Wrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5D28EB5EBE008B3BBC /* Wrapper.swift */; }; D73BCCA228EB5EC3008B3BBC /* Wrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5D28EB5EBE008B3BBC /* Wrapper.swift */; }; D73BCCA428EB5EC3008B3BBC /* Progress.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5E28EB5EBE008B3BBC /* Progress.swift */; }; D73BCCA528EB5EC3008B3BBC /* Progress.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5E28EB5EBE008B3BBC /* Progress.swift */; }; D73BCCA728EB5EC3008B3BBC /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5F28EB5EBE008B3BBC /* Blob.swift */; }; D73BCCA828EB5EC3008B3BBC /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC5F28EB5EBE008B3BBC /* Blob.swift */; }; D73BCCAA28EB5EC3008B3BBC /* OID.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6028EB5EBF008B3BBC /* OID.swift */; }; D73BCCAB28EB5EC3008B3BBC /* OID.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6028EB5EBF008B3BBC /* OID.swift */; }; D73BCCAD28EB5EC3008B3BBC /* Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6128EB5EBF008B3BBC /* Signature.swift */; }; D73BCCAE28EB5EC3008B3BBC /* Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6128EB5EBF008B3BBC /* Signature.swift */; }; D73BCCB028EB5EC3008B3BBC /* Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6228EB5EBF008B3BBC /* Object.swift */; }; D73BCCB128EB5EC3008B3BBC /* Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6228EB5EBF008B3BBC /* Object.swift */; }; D73BCCB328EB5EC3008B3BBC /* TagIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6428EB5EBF008B3BBC /* TagIterator.swift */; }; D73BCCB428EB5EC3008B3BBC /* TagIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6428EB5EBF008B3BBC /* TagIterator.swift */; }; D73BCCB628EB5EC3008B3BBC /* Tags.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6528EB5EBF008B3BBC /* Tags.swift */; }; D73BCCB728EB5EC3008B3BBC /* Tags.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6528EB5EBF008B3BBC /* Tags.swift */; }; D73BCCB928EB5EC3008B3BBC /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6628EB5EBF008B3BBC /* Tag.swift */; }; D73BCCBA28EB5EC3008B3BBC /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6628EB5EBF008B3BBC /* Tag.swift */; }; D73BCCBC28EB5EC3008B3BBC /* RevisionIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6828EB5EBF008B3BBC /* RevisionIterator.swift */; }; D73BCCBD28EB5EC3008B3BBC /* RevisionIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6828EB5EBF008B3BBC /* RevisionIterator.swift */; }; D73BCCBF28EB5EC3008B3BBC /* FileHistoryIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6928EB5EBF008B3BBC /* FileHistoryIterator.swift */; }; D73BCCC028EB5EC3008B3BBC /* FileHistoryIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6928EB5EBF008B3BBC /* FileHistoryIterator.swift */; }; D73BCCC228EB5EC3008B3BBC /* DiffEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6B28EB5EC0008B3BBC /* DiffEntry.swift */; }; D73BCCC328EB5EC3008B3BBC /* DiffEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6B28EB5EC0008B3BBC /* DiffEntry.swift */; }; D73BCCC528EB5EC4008B3BBC /* Diff.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6C28EB5EC0008B3BBC /* Diff.swift */; }; D73BCCC628EB5EC4008B3BBC /* Diff.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6C28EB5EC0008B3BBC /* Diff.swift */; }; D73BCCC828EB5EC4008B3BBC /* KeyAuthentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6E28EB5EC0008B3BBC /* KeyAuthentication.swift */; }; D73BCCC928EB5EC4008B3BBC /* KeyAuthentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6E28EB5EC0008B3BBC /* KeyAuthentication.swift */; }; D73BCCCB28EB5EC4008B3BBC /* Authentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6F28EB5EC0008B3BBC /* Authentication.swift */; }; D73BCCCC28EB5EC4008B3BBC /* Authentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC6F28EB5EC0008B3BBC /* Authentication.swift */; }; D73BCCCE28EB5EC4008B3BBC /* PasswordAuthentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7028EB5EC0008B3BBC /* PasswordAuthentication.swift */; }; D73BCCCF28EB5EC4008B3BBC /* PasswordAuthentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7028EB5EC0008B3BBC /* PasswordAuthentication.swift */; }; D73BCCD128EB5EC4008B3BBC /* Remote.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7228EB5EC0008B3BBC /* Remote.swift */; }; D73BCCD228EB5EC4008B3BBC /* Remote.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7228EB5EC0008B3BBC /* Remote.swift */; }; D73BCCD428EB5EC4008B3BBC /* Remotes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7328EB5EC0008B3BBC /* Remotes.swift */; }; D73BCCD528EB5EC4008B3BBC /* Remotes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7328EB5EC0008B3BBC /* Remotes.swift */; }; D73BCCD728EB5EC4008B3BBC /* Statuses.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7528EB5EC1008B3BBC /* Statuses.swift */; }; D73BCCD828EB5EC4008B3BBC /* Statuses.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7528EB5EC1008B3BBC /* Statuses.swift */; }; D73BCCDA28EB5EC4008B3BBC /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7628EB5EC1008B3BBC /* Status.swift */; }; D73BCCDB28EB5EC4008B3BBC /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7628EB5EC1008B3BBC /* Status.swift */; }; D73BCCDD28EB5EC4008B3BBC /* StatusIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7728EB5EC1008B3BBC /* StatusIterator.swift */; }; D73BCCDE28EB5EC4008B3BBC /* StatusIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7728EB5EC1008B3BBC /* StatusIterator.swift */; }; D73BCCE028EB5EC4008B3BBC /* Index.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7928EB5EC1008B3BBC /* Index.swift */; }; D73BCCE128EB5EC4008B3BBC /* Index.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7928EB5EC1008B3BBC /* Index.swift */; }; D73BCCE328EB5EC4008B3BBC /* Index+Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7A28EB5EC1008B3BBC /* Index+Commit.swift */; }; D73BCCE428EB5EC4008B3BBC /* Index+Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7A28EB5EC1008B3BBC /* Index+Commit.swift */; }; D73BCCE628EB5EC4008B3BBC /* Index+Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7B28EB5EC1008B3BBC /* Index+Files.swift */; }; D73BCCE728EB5EC4008B3BBC /* Index+Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7B28EB5EC1008B3BBC /* Index+Files.swift */; }; D73BCCE928EB5EC4008B3BBC /* Head+Checkout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7D28EB5EC2008B3BBC /* Head+Checkout.swift */; }; D73BCCEA28EB5EC4008B3BBC /* Head+Checkout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7D28EB5EC2008B3BBC /* Head+Checkout.swift */; }; D73BCCEC28EB5EC4008B3BBC /* Head.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7E28EB5EC2008B3BBC /* Head.swift */; }; D73BCCED28EB5EC4008B3BBC /* Head.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7E28EB5EC2008B3BBC /* Head.swift */; }; D73BCCEF28EB5EC4008B3BBC /* Head+Merge.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7F28EB5EC2008B3BBC /* Head+Merge.swift */; }; D73BCCF028EB5EC4008B3BBC /* Head+Merge.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC7F28EB5EC2008B3BBC /* Head+Merge.swift */; }; D73BCCF228EB5EC4008B3BBC /* TreeEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8128EB5EC2008B3BBC /* TreeEntry.swift */; }; D73BCCF328EB5EC4008B3BBC /* TreeEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8128EB5EC2008B3BBC /* TreeEntry.swift */; }; D73BCCF528EB5EC4008B3BBC /* Tree.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8228EB5EC2008B3BBC /* Tree.swift */; }; D73BCCF628EB5EC4008B3BBC /* Tree.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8228EB5EC2008B3BBC /* Tree.swift */; }; D73BCCF828EB5EC4008B3BBC /* RepositoryManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8428EB5EC3008B3BBC /* RepositoryManager.swift */; }; D73BCCF928EB5EC4008B3BBC /* RepositoryManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8428EB5EC3008B3BBC /* RepositoryManager.swift */; }; D73BCCFB28EB5EC4008B3BBC /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8528EB5EC3008B3BBC /* Repository.swift */; }; D73BCCFC28EB5EC4008B3BBC /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8528EB5EC3008B3BBC /* Repository.swift */; }; D73BCCFE28EB5EC4008B3BBC /* Repository+Open.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8628EB5EC3008B3BBC /* Repository+Open.swift */; }; D73BCCFF28EB5EC4008B3BBC /* Repository+Open.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8628EB5EC3008B3BBC /* Repository+Open.swift */; }; D73BCD0128EB5EC4008B3BBC /* Repository+Lookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8728EB5EC3008B3BBC /* Repository+Lookup.swift */; }; D73BCD0228EB5EC4008B3BBC /* Repository+Lookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8728EB5EC3008B3BBC /* Repository+Lookup.swift */; }; D73BCD0428EB5EC4008B3BBC /* Repository+Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8828EB5EC3008B3BBC /* Repository+Commit.swift */; }; D73BCD0528EB5EC4008B3BBC /* Repository+Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8828EB5EC3008B3BBC /* Repository+Commit.swift */; }; D73BCD0728EB5EC4008B3BBC /* Reference.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8A28EB5EC3008B3BBC /* Reference.swift */; }; D73BCD0828EB5EC4008B3BBC /* Reference.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8A28EB5EC3008B3BBC /* Reference.swift */; }; D73BCD0A28EB5EC4008B3BBC /* Reference+Target.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8B28EB5EC3008B3BBC /* Reference+Target.swift */; }; D73BCD0B28EB5EC4008B3BBC /* Reference+Target.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8B28EB5EC3008B3BBC /* Reference+Target.swift */; }; D73FABDA207F2EB600A98483 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D7465F27207F2CD600E46A52 /* Images.xcassets */; }; D73FAE9F21553CAA0058BE61 /* UIApplication+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73FAE9E21553CAA0058BE61 /* UIApplication+.swift */; }; D74112281FABA21B00AB619A /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74112271FABA21B00AB619A /* MainWindow.swift */; }; D74112291FABA29100AB619A /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74112271FABA21B00AB619A /* MainWindow.swift */; }; D743FB5324CD72B0003A8913 /* SettingsFilesNaming.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BAC62E249D11F8008D29AA /* SettingsFilesNaming.swift */; }; D743FB5424CD72B1003A8913 /* SettingsFilesNaming.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BAC62E249D11F8008D29AA /* SettingsFilesNaming.swift */; }; D7465F28207F2CD600E46A52 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D7465F27207F2CD600E46A52 /* Images.xcassets */; }; D7470D072170E890006B2A92 /* NSTextStorage++.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7470D062170E890006B2A92 /* NSTextStorage++.swift */; }; D7470D082170E890006B2A92 /* NSTextStorage++.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7470D062170E890006B2A92 /* NSTextStorage++.swift */; }; D7470D092170E890006B2A92 /* NSTextStorage++.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7470D062170E890006B2A92 /* NSTextStorage++.swift */; }; D7487F922173503C00D09383 /* AttributedBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74B7B642137D3A1007F5331 /* AttributedBox.swift */; }; D7487F932173503C00D09383 /* AttributedBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74B7B642137D3A1007F5331 /* AttributedBox.swift */; }; D7487FD721738A1800D09383 /* NSImage+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7487FD5217389F800D09383 /* NSImage+.swift */; }; D7487FD821738A1900D09383 /* NSImage+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7487FD5217389F800D09383 /* NSImage+.swift */; }; D7487FEB2174E62A00D09383 /* NSAttributedStringKey+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7487FE82174E5CB00D09383 /* NSAttributedStringKey+.swift */; }; D7487FEC2174E62B00D09383 /* NSAttributedStringKey+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7487FE82174E5CB00D09383 /* NSAttributedStringKey+.swift */; }; D7487FED2174E62B00D09383 /* NSAttributedStringKey+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7487FE82174E5CB00D09383 /* NSAttributedStringKey+.swift */; }; D74B7B672137D3A1007F5331 /* AttributedBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74B7B642137D3A1007F5331 /* AttributedBox.swift */; }; D74B7B692137EFA9007F5331 /* SingleTouchDownGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74B7B682137EFA9007F5331 /* SingleTouchDownGestureRecognizer.swift */; }; D74D479F256DF2EB00D97647 /* FSNTextAttachmentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74D479E256DF2EB00D97647 /* FSNTextAttachmentCell.swift */; }; D74D47A0256DF2EB00D97647 /* FSNTextAttachmentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74D479E256DF2EB00D97647 /* FSNTextAttachmentCell.swift */; }; D74DFBAC21661BA300F67D64 /* Date+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76E10C0215A55CE0017F4A3 /* Date+.swift */; }; D74DFBAD21661BA400F67D64 /* Date+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76E10C0215A55CE0017F4A3 /* Date+.swift */; }; D750345E285F817D00086424 /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB2920FEDE230005E120 /* String+.swift */; }; D750345F285F81F300086424 /* URL+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB2A20FEDE230005E120 /* URL+.swift */; }; D7503460285F827800086424 /* DateFormatter+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB2820FEDE230005E120 /* DateFormatter+.swift */; }; D7508FC81F337E850047AB76 /* SearchTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7508FC71F337E850047AB76 /* SearchTextField.swift */; }; D7508FCE1F3438540047AB76 /* PrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7508FCD1F3438540047AB76 /* PrefsViewController.swift */; }; D752D80823454750006842F9 /* NSTextAttachment+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D752D80723454750006842F9 /* NSTextAttachment+.swift */; }; D752D80923454750006842F9 /* NSTextAttachment+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D752D80723454750006842F9 /* NSTextAttachment+.swift */; }; D752D80B23454750006842F9 /* NSTextAttachment+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D752D80723454750006842F9 /* NSTextAttachment+.swift */; }; D75627CE26D1165A000AF6EA /* ImageFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75627CD26D1165A000AF6EA /* ImageFormat.swift */; }; D75627CF26D1165A000AF6EA /* ImageFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75627CD26D1165A000AF6EA /* ImageFormat.swift */; }; D75627D126D1165A000AF6EA /* ImageFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75627CD26D1165A000AF6EA /* ImageFormat.swift */; }; D75627D226D1165A000AF6EA /* ImageFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75627CD26D1165A000AF6EA /* ImageFormat.swift */; }; D75629B127D4DB7E00F55588 /* CodeFontViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75629B027D4DB7E00F55588 /* CodeFontViewController.swift */; }; D75629B327D4DE9F00F55588 /* CodeThemeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75629B227D4DE9F00F55588 /* CodeThemeViewController.swift */; }; D75629B527D5036D00F55588 /* SortByViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75629B427D5036D00F55588 /* SortByViewController.swift */; }; D75629B727D53EB100F55588 /* ThanksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75629B627D53EB100F55588 /* ThanksViewController.swift */; }; D75A34E527D7CD440085438F /* SidebarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75A34E427D7CD440085438F /* SidebarViewController.swift */; }; D75EE7F72078B22D0055F159 /* SidebarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75EE7F62078B22D0055F159 /* SidebarItem.swift */; }; D75EE7F82078B22D0055F159 /* SidebarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75EE7F62078B22D0055F159 /* SidebarItem.swift */; }; D75EE7FA2078B3C00055F159 /* Sidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75EE7F92078B3C00055F159 /* Sidebar.swift */; }; D75EE7FB2078B3C00055F159 /* Sidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75EE7F92078B3C00055F159 /* Sidebar.swift */; }; D75EE7FD2078C5460055F159 /* SidebarCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75EE7FC2078C5460055F159 /* SidebarCellView.swift */; }; D75EE7FE2078C5460055F159 /* SidebarCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75EE7FC2078C5460055F159 /* SidebarCellView.swift */; }; D75EE8002078E0C60055F159 /* SidebarItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75EE7FF2078E0C60055F159 /* SidebarItemType.swift */; }; D75EE8012078E0C60055F159 /* SidebarItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75EE7FF2078E0C60055F159 /* SidebarItemType.swift */; }; D75F1DF1206D660D00F70B28 /* MPreview.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D71C4A4D1F520F0E00EBA30B /* MPreview.bundle */; }; D75F3339205EC34800CC887E /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75F3338205EC34800CC887E /* ShareViewController.swift */; }; D75F3340205EC34800CC887E /* FSNotes iOS Share Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D75F3336205EC34800CC887E /* FSNotes iOS Share Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; D76025B2204EEF64000B9F59 /* TextFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76025B1204EEF64000B9F59 /* TextFormatter.swift */; }; D76025B3204EEF64000B9F59 /* TextFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76025B1204EEF64000B9F59 /* TextFormatter.swift */; }; D76025B4204EEF64000B9F59 /* TextFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76025B1204EEF64000B9F59 /* TextFormatter.swift */; }; D76447DC1F3A4F0700965F01 /* UserDefaultsManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76447DB1F3A4F0700965F01 /* UserDefaultsManagement.swift */; }; D7679376201F0BFD000F7BBF /* SortBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7679375201F0BFD000F7BBF /* SortBy.swift */; }; D7679377201F0BFD000F7BBF /* SortBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7679375201F0BFD000F7BBF /* SortBy.swift */; }; D7679389201F21F5000F7BBF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7679388201F21F5000F7BBF /* AppDelegate.swift */; }; D767938B201F21F5000F7BBF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D767938A201F21F5000F7BBF /* ViewController.swift */; }; D7680FB225D02B2C00810DA8 /* FileManager+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7680FB125D02B2C00810DA8 /* FileManager+.swift */; }; D7680FB325D02B2C00810DA8 /* FileManager+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7680FB125D02B2C00810DA8 /* FileManager+.swift */; }; D7680FB525D02B2C00810DA8 /* FileManager+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7680FB125D02B2C00810DA8 /* FileManager+.swift */; }; D7680FB625D02B2C00810DA8 /* FileManager+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7680FB125D02B2C00810DA8 /* FileManager+.swift */; }; D768D758245E86670028F344 /* NSAttributedString+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D768D754245E854D0028F344 /* NSAttributedString+.swift */; }; D768D759245E86680028F344 /* NSAttributedString+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D768D754245E854D0028F344 /* NSAttributedString+.swift */; }; D768D75A245E86690028F344 /* NSAttributedString+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D768D754245E854D0028F344 /* NSAttributedString+.swift */; }; D768D75C245ED6470028F344 /* VerticallyAlignedTextFieldCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D768D75B245ED6470028F344 /* VerticallyAlignedTextFieldCell.swift */; }; D768D75D245ED6470028F344 /* VerticallyAlignedTextFieldCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D768D75B245ED6470028F344 /* VerticallyAlignedTextFieldCell.swift */; }; D76E10C1215A55CE0017F4A3 /* Date+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76E10C0215A55CE0017F4A3 /* Date+.swift */; }; D76F3682272563EC00D1FFB4 /* NSAttributedString+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D768D754245E854D0028F344 /* NSAttributedString+.swift */; }; D77015822C972B7500CFF0E8 /* SettingsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77015812C972B6D00CFF0E8 /* SettingsTableViewCell.swift */; }; D771E96F28EDFBF600CD4871 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = D771E96E28EDFBF600CD4871 /* Errors.swift */; }; D771E97028EDFBF600CD4871 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = D771E96E28EDFBF600CD4871 /* Errors.swift */; }; D772C8842217362C007E440B /* ViewController+Print.swift in Sources */ = {isa = PBXBuildFile; fileRef = D772C8832217362C007E440B /* ViewController+Print.swift */; }; D772C8852217362C007E440B /* ViewController+Print.swift in Sources */ = {isa = PBXBuildFile; fileRef = D772C8832217362C007E440B /* ViewController+Print.swift */; }; D7737393223D59CF00154B9E /* KeychainConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD26222BF30700E69C93 /* KeychainConfiguration.swift */; }; D7737394223D59D300154B9E /* KeychainPasswordItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D730BD29222BF32A00E69C93 /* KeychainPasswordItem.swift */; }; D773DE801F36F45900A39C9F /* SandboxBookmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = D773DE7F1F36F45900A39C9F /* SandboxBookmark.swift */; }; D7767C7F234E47B9006A0716 /* Markdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7136ED23490CBF004DFA6E /* Markdown.swift */; }; D777D7812009115C00D86B33 /* ImagesProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D777D7802009115C00D86B33 /* ImagesProcessor.swift */; }; D777D782200912A400D86B33 /* ImagesProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D777D7802009115C00D86B33 /* ImagesProcessor.swift */; }; D7793C731F211C6000CA39B7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7793C721F211C6000CA39B7 /* AppDelegate.swift */; }; D7793C751F211C6000CA39B7 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7793C741F211C6000CA39B7 /* ViewController.swift */; }; D779C7BB1F415C0300FADEE1 /* PrefsWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D779C7BA1F415BE300FADEE1 /* PrefsWindowController.swift */; }; D77A12372C469ACF001B388B /* SearchQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = D714749C279D7DBC001A8B29 /* SearchQuery.swift */; }; D77A12382C469AD0001B388B /* SearchQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = D714749C279D7DBC001A8B29 /* SearchQuery.swift */; }; D77A6F7F28B11496006A0353 /* PreferencesWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77A6F7E28B11496006A0353 /* PreferencesWebViewController.swift */; }; D77A6F8028B11496006A0353 /* PreferencesWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77A6F7E28B11496006A0353 /* PreferencesWebViewController.swift */; }; D77AD7FC27F9D1C90077BD45 /* Data+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77AD7FB27F9D1C90077BD45 /* Data+.swift */; }; D77AD7FD27F9D1C90077BD45 /* Data+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77AD7FB27F9D1C90077BD45 /* Data+.swift */; }; D77AD7FF27F9D1C90077BD45 /* Data+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77AD7FB27F9D1C90077BD45 /* Data+.swift */; }; D77CC041216A608500582B97 /* EditorScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77CC040216A608500582B97 /* EditorScrollView.swift */; }; D77CC042216A608500582B97 /* EditorScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77CC040216A608500582B97 /* EditorScrollView.swift */; }; D77E0536246312B200AD7772 /* StorageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77E05282463124300AD7772 /* StorageType.swift */; }; D77E0537246312B300AD7772 /* StorageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77E05282463124300AD7772 /* StorageType.swift */; }; D77E0538246312B400AD7772 /* StorageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77E05282463124300AD7772 /* StorageType.swift */; }; D77E0539246312B400AD7772 /* StorageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77E05282463124300AD7772 /* StorageType.swift */; }; D77F41B32A0D48F500E2B7A2 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D77F41B22A0D48F500E2B7A2 /* Launch Screen.storyboard */; }; D77F89DF28D38B5D00BECC87 /* ViewController+Web.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77F89DE28D38B5D00BECC87 /* ViewController+Web.swift */; }; D77F89E028D38B5E00BECC87 /* ViewController+Web.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77F89DE28D38B5D00BECC87 /* ViewController+Web.swift */; }; D78115632153B36C004FA1CA /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78115622153B36C004FA1CA /* Buttons.swift */; }; D78115672153D4D9004FA1CA /* ProjectsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78115662153D4D9004FA1CA /* ProjectsViewController.swift */; }; D781156B2153E05A004FA1CA /* ProjectSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D781156A2153E05A004FA1CA /* ProjectSettingsViewController.swift */; }; D783B505208A1BFD00328A41 /* EditorSplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D783B504208A1BFD00328A41 /* EditorSplitView.swift */; }; D783B506208A1BFD00328A41 /* EditorSplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D783B504208A1BFD00328A41 /* EditorSplitView.swift */; }; D785805C27A3483B000C1BAF /* FolderPopoverActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D785805B27A3483B000C1BAF /* FolderPopoverActions.swift */; }; D78678CB2093AE10001A6620 /* UndoData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78678CA2093AE10001A6620 /* UndoData.swift */; }; D78678CC2093AE10001A6620 /* UndoData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78678CA2093AE10001A6620 /* UndoData.swift */; }; D792297521A845B4005F468F /* ProjectSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D792297421A845B4005F468F /* ProjectSettingsViewController.swift */; }; D792297621A845B4005F468F /* ProjectSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D792297421A845B4005F468F /* ProjectSettingsViewController.swift */; }; D792DD8227A6C980006ADC01 /* FSParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = D792DD8127A6C980006ADC01 /* FSParser.swift */; }; D792DD8327A6C980006ADC01 /* FSParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = D792DD8127A6C980006ADC01 /* FSParser.swift */; }; D792DD8527A6C980006ADC01 /* FSParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = D792DD8127A6C980006ADC01 /* FSParser.swift */; }; D792DD8627A6C980006ADC01 /* FSParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = D792DD8127A6C980006ADC01 /* FSParser.swift */; }; D792DD9427A6D6F5006ADC01 /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB2920FEDE230005E120 /* String+.swift */; }; D792DD9627A6D71C006ADC01 /* String+Punycode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D792DD9527A6D71C006ADC01 /* String+Punycode.swift */; }; D792DD9727A6D71C006ADC01 /* String+Punycode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D792DD9527A6D71C006ADC01 /* String+Punycode.swift */; }; D792DD9927A6D71C006ADC01 /* String+Punycode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D792DD9527A6D71C006ADC01 /* String+Punycode.swift */; }; D794558E27C05743000C283F /* ProViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D794558D27C05743000C283F /* ProViewController.swift */; }; D794559A27C1B3F9000C283F /* ExternalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D794559927C1B3F9000C283F /* ExternalViewController.swift */; }; D7958A3922ED512D00EDBDDC /* SandboxBookmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7958A3822ED512D00EDBDDC /* SandboxBookmark.swift */; }; D7958A3A22ED512D00EDBDDC /* SandboxBookmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7958A3822ED512D00EDBDDC /* SandboxBookmark.swift */; }; D79651B12517741400333AD4 /* ProgressState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79651B02517741400333AD4 /* ProgressState.swift */; }; D79651B22517741400333AD4 /* ProgressState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79651B02517741400333AD4 /* ProgressState.swift */; }; D79651B42517741400333AD4 /* ProgressState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79651B02517741400333AD4 /* ProgressState.swift */; }; D79651B52517741400333AD4 /* ProgressState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79651B02517741400333AD4 /* ProgressState.swift */; }; D796EB41251E127300CE5C80 /* Pasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D796EB40251E127300CE5C80 /* Pasteboard.swift */; }; D796EB42251E127300CE5C80 /* Pasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D796EB40251E127300CE5C80 /* Pasteboard.swift */; }; D797004C1F3DD10700BAD94D /* EditTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D797004B1F3DD10700BAD94D /* EditTextView.swift */; }; D79798A229C0FE6A00B9A878 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79798A129C0FE6A00B9A878 /* SettingsViewController.swift */; }; D79798A329C0FE6A00B9A878 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79798A129C0FE6A00B9A878 /* SettingsViewController.swift */; }; D79A13CD2A0E9C980037510B /* UIColor+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79A13CC2A0E9C980037510B /* UIColor+.swift */; }; D79A13CE2A0E9C980037510B /* UIColor+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79A13CC2A0E9C980037510B /* UIColor+.swift */; }; D79C26252872384C00CB70E6 /* EditorViewController+Sharing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79C26242872384C00CB70E6 /* EditorViewController+Sharing.swift */; }; D79C26262872384C00CB70E6 /* EditorViewController+Sharing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79C26242872384C00CB70E6 /* EditorViewController+Sharing.swift */; }; D79F92121FA8B9E2008C297E /* PreviewTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CE196B1FA4BA5E004BF8EE /* PreviewTextField.swift */; }; D79FE8A21F77D04A00113CFD /* Note.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79FE8A01F77D04A00113CFD /* Note.swift */; }; D7A415141F2FBDA00099B82C /* NotesTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A415131F2FBDA00099B82C /* NotesTableView.swift */; }; D7A549C324DD9D3400537544 /* SettingsEditorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A549C224DD9D3400537544 /* SettingsEditorViewController.swift */; }; D7A65C5920F11C38003E5ADC /* LanguageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A65C5820F11C38003E5ADC /* LanguageType.swift */; }; D7A65C5A20F11C38003E5ADC /* LanguageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A65C5820F11C38003E5ADC /* LanguageType.swift */; }; D7A9C1D62910784400905619 /* Project+Git.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A9C1D52910784400905619 /* Project+Git.swift */; }; D7A9C1D72910784400905619 /* Project+Git.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A9C1D52910784400905619 /* Project+Git.swift */; }; D7A9C1D92910784400905619 /* Project+Git.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A9C1D52910784400905619 /* Project+Git.swift */; }; D7A9C1DB29107A0800905619 /* Git in Frameworks */ = {isa = PBXBuildFile; productRef = D7A9C1DA29107A0800905619 /* Git */; }; D7A9C1DC29107B7600905619 /* Reference+Target.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BCC8B28EB5EC3008B3BBC /* Reference+Target.swift */; }; D7ADFD112066CF9400B531F9 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7ADFD102066CF9400B531F9 /* CoreLocation.framework */; }; D7B13DBD2C64F445008EBCAA /* Printer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B13DBC2C64F445008EBCAA /* Printer.swift */; }; D7B13DBE2C64F445008EBCAA /* Printer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B13DBC2C64F445008EBCAA /* Printer.swift */; }; D7B2B6EA245EEA620084B78D /* LanguageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A65C5820F11C38003E5ADC /* LanguageType.swift */; }; D7B2B6EC245EEA790084B78D /* LanguageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A65C5820F11C38003E5ADC /* LanguageType.swift */; }; D7B34F9725195D7E0007877E /* PreviewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B34F9625195D7E0007877E /* PreviewState.swift */; }; D7B34F9825195D7E0007877E /* PreviewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B34F9625195D7E0007877E /* PreviewState.swift */; }; D7B34F9A25195D7E0007877E /* PreviewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B34F9625195D7E0007877E /* PreviewState.swift */; }; D7B34F9B25195D7E0007877E /* PreviewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B34F9625195D7E0007877E /* PreviewState.swift */; }; D7B3FE7021027A5E00764C39 /* UserDataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB3820FEDE230005E120 /* UserDataService.swift */; }; D7B3FE7121027A6D00764C39 /* UserDataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB3820FEDE230005E120 /* UserDataService.swift */; }; D7B4AC5E2471253100F3888A /* NoteMeta.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B4AC5D2471253100F3888A /* NoteMeta.swift */; }; D7B4AC5F2471253100F3888A /* NoteMeta.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B4AC5D2471253100F3888A /* NoteMeta.swift */; }; D7B4AC612471253100F3888A /* NoteMeta.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B4AC5D2471253100F3888A /* NoteMeta.swift */; }; D7B4AC622471253100F3888A /* NoteMeta.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B4AC5D2471253100F3888A /* NoteMeta.swift */; }; D7B6E59A207912E300FE0E20 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B6E599207912E300FE0E20 /* Project.swift */; }; D7B6E59B207912E300FE0E20 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B6E599207912E300FE0E20 /* Project.swift */; }; D7B6E59D20794B8C00FE0E20 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B6E599207912E300FE0E20 /* Project.swift */; }; D7BA204D2186E3DD0064824B /* TitleBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2799407B218484C900727B20 /* TitleBarView.swift */; }; D7BAC62F249D11F8008D29AA /* SettingsFilesNaming.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BAC62E249D11F8008D29AA /* SettingsFilesNaming.swift */; }; D7BAC631249D1204008D29AA /* SettingsFilesNaming.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BAC62E249D11F8008D29AA /* SettingsFilesNaming.swift */; }; D7BB2DFE29A0157700D5055A /* Storage+Git.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BB2DFD29A0157700D5055A /* Storage+Git.swift */; }; D7BB2DFF29A0157700D5055A /* Storage+Git.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BB2DFD29A0157700D5055A /* Storage+Git.swift */; }; D7BB2E0129A0157700D5055A /* Storage+Git.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BB2DFD29A0157700D5055A /* Storage+Git.swift */; }; D7BCF035296B0DAA00F72A4F /* AboutImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BCF034296B0DAA00F72A4F /* AboutImageView.swift */; }; D7BCF036296B0DAA00F72A4F /* AboutImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BCF034296B0DAA00F72A4F /* AboutImageView.swift */; }; D7BDFE59201F671900897A58 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E025071F3B6DDB00EDDA32 /* Storage.swift */; }; D7BDFE5D201F677B00897A58 /* UserDefaultsManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76447DB1F3A4F0700965F01 /* UserDefaultsManagement.swift */; }; D7BDFE60201F677B00897A58 /* NoteType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D708AC662000EF5800A1760F /* NoteType.swift */; }; D7BDFE61201F677B00897A58 /* SortBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7679375201F0BFD000F7BBF /* SortBy.swift */; }; D7BDFE62201F678C00897A58 /* Note.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79FE8A01F77D04A00113CFD /* Note.swift */; }; D7BDFE68201F67DA00897A58 /* NotesTextProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F95F391FF2759300E2A447 /* NotesTextProcessor.swift */; }; D7BDFE6A201F68B700897A58 /* NotesTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BDFE69201F68B700897A58 /* NotesTableView.swift */; }; D7BDFE6C201F6DC200897A58 /* EditTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BDFE6B201F6DC200897A58 /* EditTextView.swift */; }; D7BDFE70201F788D00897A58 /* NoteCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BDFE6F201F788D00897A58 /* NoteCellView.swift */; }; D7C1C99A235606CB0021A32D /* SidebarHeaderCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C1C999235606CB0021A32D /* SidebarHeaderCellView.swift */; }; D7C1C99B235606CB0021A32D /* SidebarHeaderCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C1C999235606CB0021A32D /* SidebarHeaderCellView.swift */; }; D7C33F6E29E09A690006C473 /* AppIconViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C33F6D29E09A690006C473 /* AppIconViewController.swift */; }; D7C6DB5B25AA880600F8F76F /* ViewController+More.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C6DB5A25AA880600F8F76F /* ViewController+More.swift */; }; D7C803EE2046DBBD005DA599 /* DefaultExtensionControllerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C803ED2046DBBD005DA599 /* DefaultExtensionControllerView.swift */; }; D7C9029223547A1E00A89BD8 /* FSTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C9029123547A1E00A89BD8 /* FSTag.swift */; }; D7C9029323547A1E00A89BD8 /* FSTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C9029123547A1E00A89BD8 /* FSTag.swift */; }; D7C9029523547A1E00A89BD8 /* FSTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C9029123547A1E00A89BD8 /* FSTag.swift */; }; D7CA7FD4232652E300E9717A /* PreferencesGitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CA7FD3232652E300E9717A /* PreferencesGitViewController.swift */; }; D7CA7FD5232652E300E9717A /* PreferencesGitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CA7FD3232652E300E9717A /* PreferencesGitViewController.swift */; }; D7CB9905207E5AE300037E91 /* SidebarTableRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CB9904207E5AE300037E91 /* SidebarTableRowView.swift */; }; D7CB9906207E5AE300037E91 /* SidebarTableRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CB9904207E5AE300037E91 /* SidebarTableRowView.swift */; }; D7CBAFFE214D5A1C002ECD5A /* ShortcutIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CBAFFD214D5A1C002ECD5A /* ShortcutIdentifier.swift */; }; D7CC44C12A1E5E4F00743857 /* ViewController+WebApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CC44C02A1E5E4F00743857 /* ViewController+WebApi.swift */; }; D7CC44C22A1E5E4F00743857 /* ViewController+WebApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CC44C02A1E5E4F00743857 /* ViewController+WebApi.swift */; }; D7CC44C42A1E5E4F00743857 /* ViewController+WebApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CC44C02A1E5E4F00743857 /* ViewController+WebApi.swift */; }; D7CC44C62A1E5F7600743857 /* ApiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CC44C52A1E5F7600743857 /* ApiResponse.swift */; }; D7CC44C72A1E5F7600743857 /* ApiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CC44C52A1E5F7600743857 /* ApiResponse.swift */; }; D7CC44C92A1E5F7600743857 /* ApiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CC44C52A1E5F7600743857 /* ApiResponse.swift */; }; D7CCEDB92C6BA2F300A3BB83 /* ClickableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CCEDB82C6BA2F300A3BB83 /* ClickableTextField.swift */; }; D7CCEDBA2C6BA2F300A3BB83 /* ClickableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CCEDB82C6BA2F300A3BB83 /* ClickableTextField.swift */; }; D7CD5CC42181F7530009D63B /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E025071F3B6DDB00EDDA32 /* Storage.swift */; }; D7CD5CC5218209820009D63B /* UserDefaultsManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76447DB1F3A4F0700965F01 /* UserDefaultsManagement.swift */; }; D7CD5CC6218209960009D63B /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B6E599207912E300FE0E20 /* Project.swift */; }; D7CD5CC7218209BD0009D63B /* SortBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7679375201F0BFD000F7BBF /* SortBy.swift */; }; D7CD5CC9218209D80009D63B /* NoteType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D708AC662000EF5800A1760F /* NoteType.swift */; }; D7CD5CCC21820B7A0009D63B /* UserDefaultsManagement+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CD5CCA21820A380009D63B /* UserDefaultsManagement+.swift */; }; D7CD5CCD21820B7B0009D63B /* UserDefaultsManagement+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CD5CCA21820A380009D63B /* UserDefaultsManagement+.swift */; }; D7CD5CCE21820C9D0009D63B /* DateFormatter+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB2820FEDE230005E120 /* DateFormatter+.swift */; }; D7CD5CD021820CBC0009D63B /* URL+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F13BB2A20FEDE230005E120 /* URL+.swift */; }; D7CD5CD121820CCD0009D63B /* Date+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76E10C0215A55CE0017F4A3 /* Date+.swift */; }; D7CD5CD421820D640009D63B /* NSAttributedStringKey+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7487FE82174E5CB00D09383 /* NSAttributedStringKey+.swift */; }; D7CD5CD521820D700009D63B /* ImagesProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D777D7802009115C00D86B33 /* ImagesProcessor.swift */; }; D7CD5CDB21832C190009D63B /* UserDefaultsManagement+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CD5CDA21832C190009D63B /* UserDefaultsManagement+.swift */; }; D7CD5CDC21832C190009D63B /* UserDefaultsManagement+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CD5CDA21832C190009D63B /* UserDefaultsManagement+.swift */; }; D7CD5F681F508E74006AA35D /* SourceCodePro-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7CD5F671F508E6A006AA35D /* SourceCodePro-Bold.ttf */; }; D7CD5F691F508E74006AA35D /* SourceCodePro-BoldIt.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7CD5F661F508E6A006AA35D /* SourceCodePro-BoldIt.ttf */; }; D7CD5F6C1F51185F006AA35D /* NSFont+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CD5F6B1F51185F006AA35D /* NSFont+.swift */; }; D7CDE9DC2161767A00DC5978 /* AppearanceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CDE9DB2161767A00DC5978 /* AppearanceType.swift */; }; D7CDE9DD2161767B00DC5978 /* AppearanceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CDE9DB2161767A00DC5978 /* AppearanceType.swift */; }; D7CDE9DE216178DE00DC5978 /* AppearanceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CDE9DB2161767A00DC5978 /* AppearanceType.swift */; }; D7CE196C1FA4BA5E004BF8EE /* PreviewTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CE196B1FA4BA5E004BF8EE /* PreviewTextField.swift */; }; D7CF7EAB29E2093C00FEC0C5 /* SecurityViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CF7EAA29E2093C00FEC0C5 /* SecurityViewController.swift */; }; D7D01AFD2C65203A00F545D0 /* PrinterLegacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D01AFC2C65203A00F545D0 /* PrinterLegacy.swift */; }; D7D01AFE2C65208000F545D0 /* PrinterLegacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D01AFC2C65203A00F545D0 /* PrinterLegacy.swift */; }; D7D03BAF205C250500D96A6D /* FontViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D03BAE205C250500D96A6D /* FontViewController.swift */; }; D7D1DE68216D05A800AC1845 /* NameTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D1DE67216D05A800AC1845 /* NameTextField.swift */; }; D7D1DE69216D05A800AC1845 /* NameTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D1DE67216D05A800AC1845 /* NameTextField.swift */; }; D7D2F27E2B54BD42003DCA47 /* Shout in Frameworks */ = {isa = PBXBuildFile; productRef = D7D2F27D2B54BD42003DCA47 /* Shout */; }; D7D2F2802B54BDD4003DCA47 /* Shout in Frameworks */ = {isa = PBXBuildFile; productRef = D7D2F27F2B54BDD4003DCA47 /* Shout */; }; D7D2F2822B54BE59003DCA47 /* Shout in Frameworks */ = {isa = PBXBuildFile; productRef = D7D2F2812B54BE59003DCA47 /* Shout */; }; D7D372F4207B5B0F00AFBD9F /* SidebarNotesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D372F3207B5B0F00AFBD9F /* SidebarNotesView.swift */; }; D7D372F5207B5B0F00AFBD9F /* SidebarNotesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D372F3207B5B0F00AFBD9F /* SidebarNotesView.swift */; }; D7D372F7207BB09500AFBD9F /* SidebarOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D372F6207BB09500AFBD9F /* SidebarOutlineView.swift */; }; D7D372F8207BB09500AFBD9F /* SidebarOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D372F6207BB09500AFBD9F /* SidebarOutlineView.swift */; }; D7D3D2A427BEEA61001C1497 /* Note+History.swift in Sources */ = {isa = PBXBuildFile; fileRef = D736DDAC27BAC7940012ED70 /* Note+History.swift */; }; D7D3D2A527BEEA62001C1497 /* Note+History.swift in Sources */ = {isa = PBXBuildFile; fileRef = D736DDAC27BAC7940012ED70 /* Note+History.swift */; }; D7D79C28236798C300898A2D /* Welcome.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D7D79C27236798C300898A2D /* Welcome.bundle */; }; D7D79C29236798C300898A2D /* Welcome.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D7D79C27236798C300898A2D /* Welcome.bundle */; }; D7D7CD3A232774BC0016AC15 /* ViewController+Git.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D7CD39232774BC0016AC15 /* ViewController+Git.swift */; }; D7D7CD3B232774BC0016AC15 /* ViewController+Git.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D7CD39232774BC0016AC15 /* ViewController+Git.swift */; }; D7D9503D209D806F001FB60B /* SidebarTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D9503C209D806F001FB60B /* SidebarTableView.swift */; }; D7D9503F209D846E001FB60B /* SidebarTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D9503E209D846E001FB60B /* SidebarTableCellView.swift */; }; D7D97F3C290437A200C651D4 /* NSWindowController+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D97F3B290437A200C651D4 /* NSWindowController+.swift */; }; D7D97F3D290437A200C651D4 /* NSWindowController+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D97F3B290437A200C651D4 /* NSWindowController+.swift */; }; D7DA9E1E21031901001CF0BE /* OutlineHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA9E1D21031901001CF0BE /* OutlineHeaderView.swift */; }; D7DA9E1F21031901001CF0BE /* OutlineHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA9E1D21031901001CF0BE /* OutlineHeaderView.swift */; }; D7DA9E2121033489001CF0BE /* NSMutableAttributedString+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA9E2021033489001CF0BE /* NSMutableAttributedString+.swift */; }; D7DA9E2221033489001CF0BE /* NSMutableAttributedString+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA9E2021033489001CF0BE /* NSMutableAttributedString+.swift */; }; D7DA9E2321033834001CF0BE /* NSMutableAttributedString+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA9E2021033489001CF0BE /* NSMutableAttributedString+.swift */; }; D7DD5A881F88D4EA00CE947E /* FileWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DD5A871F88D4EA00CE947E /* FileWatcher.swift */; }; D7DD5A8A1F88D50000CE947E /* FileWatcherEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DD5A891F88D50000CE947E /* FileWatcherEvent.swift */; }; D7DD79581F4E60D000D5724B /* SourceCodePro-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7DD79561F4E606600D5724B /* SourceCodePro-Black.ttf */; }; D7DD79591F4E60D000D5724B /* SourceCodePro-It.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7DD79551F4E606600D5724B /* SourceCodePro-It.ttf */; }; D7DD795B1F4E611D00D5724B /* SourceCodePro-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7DD795A1F4E611200D5724B /* SourceCodePro-Regular.ttf */; }; D7E025081F3B6DDB00EDDA32 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E025071F3B6DDB00EDDA32 /* Storage.swift */; }; D7E32C2C28F8D0740048614B /* StaticSshKeyDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E32C2B28F8D0740048614B /* StaticSshKeyDelegate.swift */; }; D7E32C2D28F8D0750048614B /* StaticSshKeyDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E32C2B28F8D0740048614B /* StaticSshKeyDelegate.swift */; }; D7E51713220D814D00A9CAD9 /* UTI.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E51712220D814D00A9CAD9 /* UTI.swift */; }; D7E51714220D814D00A9CAD9 /* UTI.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E51712220D814D00A9CAD9 /* UTI.swift */; }; D7E6ACE920832D41003599A2 /* AppDelegate+URLRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E6ACE820832D40003599A2 /* AppDelegate+URLRoutes.swift */; }; D7E6ACEA20832D41003599A2 /* AppDelegate+URLRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E6ACE820832D40003599A2 /* AppDelegate+URLRoutes.swift */; }; D7E6D9D320808623003ECAFC /* SidebarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75EE7F62078B22D0055F159 /* SidebarItem.swift */; }; D7E6D9D42080862F003ECAFC /* SidebarItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75EE7FF2078E0C60055F159 /* SidebarItemType.swift */; }; D7E7DB3327A9B17000408725 /* DatePickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E7DB3227A9B16F00408725 /* DatePickerViewController.swift */; }; D7E81C2E1F925B5F00416A91 /* PrefsWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D779C7BA1F415BE300FADEE1 /* PrefsWindowController.swift */; }; D7E81C2F1F925B5F00416A91 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7793C741F211C6000CA39B7 /* ViewController.swift */; }; D7E81C301F925B5F00416A91 /* FileWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DD5A871F88D4EA00CE947E /* FileWatcher.swift */; }; D7E81C321F925B5F00416A91 /* PrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7508FCD1F3438540047AB76 /* PrefsViewController.swift */; }; D7E81C331F925B5F00416A91 /* UserDefaultsManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76447DB1F3A4F0700965F01 /* UserDefaultsManagement.swift */; }; D7E81C341F925B5F00416A91 /* EditTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D797004B1F3DD10700BAD94D /* EditTextView.swift */; }; D7E81C361F925B5F00416A91 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7793C721F211C6000CA39B7 /* AppDelegate.swift */; }; D7E81C371F925B5F00416A91 /* NoteRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D735E5BE1F2F001500173215 /* NoteRowView.swift */; }; D7E81C381F925B5F00416A91 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E025071F3B6DDB00EDDA32 /* Storage.swift */; }; D7E81C391F925B5F00416A91 /* FileWatcherEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DD5A891F88D50000CE947E /* FileWatcherEvent.swift */; }; D7E81C3A1F925B5F00416A91 /* NotesTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A415131F2FBDA00099B82C /* NotesTableView.swift */; }; D7E81C3D1F925B5F00416A91 /* SandboxBookmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = D773DE7F1F36F45900A39C9F /* SandboxBookmark.swift */; }; D7E81C3F1F925B5F00416A91 /* NSFont+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CD5F6B1F51185F006AA35D /* NSFont+.swift */; }; D7E81C411F925B5F00416A91 /* SearchTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7508FC71F337E850047AB76 /* SearchTextField.swift */; }; D7E81C421F925B5F00416A91 /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275592961F3AE9B5006B8988 /* MainWindowController.swift */; }; D7E81C441F925B5F00416A91 /* NoteCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D735E5BC1F2EF66000173215 /* NoteCellView.swift */; }; D7E81C461F925B5F00416A91 /* Note.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79FE8A01F77D04A00113CFD /* Note.swift */; }; D7E81C4B1F925B5F00416A91 /* MPreview.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D71C4A4D1F520F0E00EBA30B /* MPreview.bundle */; }; D7E81C4C1F925B5F00416A91 /* SourceCodePro-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7CD5F671F508E6A006AA35D /* SourceCodePro-Bold.ttf */; }; D7E81C4D1F925B5F00416A91 /* SourceCodePro-BoldIt.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7CD5F661F508E6A006AA35D /* SourceCodePro-BoldIt.ttf */; }; D7E81C4E1F925B5F00416A91 /* SourceCodePro-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7DD795A1F4E611200D5724B /* SourceCodePro-Regular.ttf */; }; D7E81C4F1F925B5F00416A91 /* SourceCodePro-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7DD79561F4E606600D5724B /* SourceCodePro-Black.ttf */; }; D7E81C501F925B5F00416A91 /* SourceCodePro-It.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7DD79551F4E606600D5724B /* SourceCodePro-It.ttf */; }; D7E81C531F925B5F00416A91 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D7793C781F211C6000CA39B7 /* Main.storyboard */; }; D7E9FEBA2C4AA59D0025D8E3 /* SidebarItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75EE7FF2078E0C60055F159 /* SidebarItemType.swift */; }; D7E9FEBC2C4AA64B0025D8E3 /* SearchQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = D714749C279D7DBC001A8B29 /* SearchQuery.swift */; }; D7ECE68A22B6B481006A14C6 /* TextBundle.icns in Resources */ = {isa = PBXBuildFile; fileRef = D720240B22A9412B000A7691 /* TextBundle.icns */; }; D7EDEDFB219203C9000B8C1A /* NoteCellView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EDEDFA219203C9000B8C1A /* NoteCellView+.swift */; }; D7EDEDFC219203C9000B8C1A /* NoteCellView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EDEDFA219203C9000B8C1A /* NoteCellView+.swift */; }; D7EDEDFD21920402000B8C1A /* NoteCellView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EDEDFA219203C9000B8C1A /* NoteCellView+.swift */; }; D7F2F19721C503F000E41811 /* AvenirNext-MediumItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18721C503EF00E41811 /* AvenirNext-MediumItalic.ttf */; }; D7F2F19821C503F000E41811 /* AvenirNext-MediumItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18721C503EF00E41811 /* AvenirNext-MediumItalic.ttf */; }; D7F2F19921C503F000E41811 /* AvenirNext-MediumItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18721C503EF00E41811 /* AvenirNext-MediumItalic.ttf */; }; D7F2F19A21C503F000E41811 /* AvenirNext-UltraLight.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18821C503EF00E41811 /* AvenirNext-UltraLight.ttf */; }; D7F2F19B21C503F000E41811 /* AvenirNext-UltraLight.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18821C503EF00E41811 /* AvenirNext-UltraLight.ttf */; }; D7F2F19C21C503F000E41811 /* AvenirNext-UltraLight.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18821C503EF00E41811 /* AvenirNext-UltraLight.ttf */; }; D7F2F19D21C503F000E41811 /* AvenirNext-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18921C503EF00E41811 /* AvenirNext-Medium.ttf */; }; D7F2F19E21C503F000E41811 /* AvenirNext-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18921C503EF00E41811 /* AvenirNext-Medium.ttf */; }; D7F2F19F21C503F000E41811 /* AvenirNext-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18921C503EF00E41811 /* AvenirNext-Medium.ttf */; }; D7F2F1A021C503F000E41811 /* AvenirNext-Heavy.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18A21C503EF00E41811 /* AvenirNext-Heavy.ttf */; }; D7F2F1A121C503F000E41811 /* AvenirNext-Heavy.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18A21C503EF00E41811 /* AvenirNext-Heavy.ttf */; }; D7F2F1A221C503F000E41811 /* AvenirNext-Heavy.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18A21C503EF00E41811 /* AvenirNext-Heavy.ttf */; }; D7F2F1A321C503F000E41811 /* AvenirNext-DemiItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18B21C503EF00E41811 /* AvenirNext-DemiItalic.ttf */; }; D7F2F1A421C503F000E41811 /* AvenirNext-DemiItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18B21C503EF00E41811 /* AvenirNext-DemiItalic.ttf */; }; D7F2F1A521C503F000E41811 /* AvenirNext-DemiItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18B21C503EF00E41811 /* AvenirNext-DemiItalic.ttf */; }; D7F2F1A621C503F000E41811 /* AvenirNext-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18C21C503EF00E41811 /* AvenirNext-BoldItalic.ttf */; }; D7F2F1A721C503F000E41811 /* AvenirNext-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18C21C503EF00E41811 /* AvenirNext-BoldItalic.ttf */; }; D7F2F1A821C503F000E41811 /* AvenirNext-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18C21C503EF00E41811 /* AvenirNext-BoldItalic.ttf */; }; D7F2F1A921C503F000E41811 /* AvenirNext-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18D21C503EF00E41811 /* AvenirNext-Regular.ttf */; }; D7F2F1AA21C503F000E41811 /* AvenirNext-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18D21C503EF00E41811 /* AvenirNext-Regular.ttf */; }; D7F2F1AB21C503F000E41811 /* AvenirNext-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18D21C503EF00E41811 /* AvenirNext-Regular.ttf */; }; D7F2F1AC21C503F000E41811 /* AvenirNext-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18E21C503EF00E41811 /* AvenirNext-Light.ttf */; }; D7F2F1AD21C503F000E41811 /* AvenirNext-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18E21C503EF00E41811 /* AvenirNext-Light.ttf */; }; D7F2F1AE21C503F000E41811 /* AvenirNext-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18E21C503EF00E41811 /* AvenirNext-Light.ttf */; }; D7F2F1AF21C503F000E41811 /* AvenirNext-Demi.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18F21C503EF00E41811 /* AvenirNext-Demi.ttf */; }; D7F2F1B021C503F000E41811 /* AvenirNext-Demi.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18F21C503EF00E41811 /* AvenirNext-Demi.ttf */; }; D7F2F1B121C503F000E41811 /* AvenirNext-Demi.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F18F21C503EF00E41811 /* AvenirNext-Demi.ttf */; }; D7F2F1B221C503F000E41811 /* AvenirNext-HeavyItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19021C503EF00E41811 /* AvenirNext-HeavyItalic.ttf */; }; D7F2F1B321C503F000E41811 /* AvenirNext-HeavyItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19021C503EF00E41811 /* AvenirNext-HeavyItalic.ttf */; }; D7F2F1B421C503F000E41811 /* AvenirNext-HeavyItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19021C503EF00E41811 /* AvenirNext-HeavyItalic.ttf */; }; D7F2F1B521C503F000E41811 /* AvenirNext-ThinItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19121C503F000E41811 /* AvenirNext-ThinItalic.ttf */; }; D7F2F1B621C503F000E41811 /* AvenirNext-ThinItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19121C503F000E41811 /* AvenirNext-ThinItalic.ttf */; }; D7F2F1B721C503F000E41811 /* AvenirNext-ThinItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19121C503F000E41811 /* AvenirNext-ThinItalic.ttf */; }; D7F2F1B821C503F000E41811 /* AvenirNext-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19221C503F000E41811 /* AvenirNext-Bold.ttf */; }; D7F2F1B921C503F000E41811 /* AvenirNext-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19221C503F000E41811 /* AvenirNext-Bold.ttf */; }; D7F2F1BA21C503F000E41811 /* AvenirNext-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19221C503F000E41811 /* AvenirNext-Bold.ttf */; }; D7F2F1BB21C503F000E41811 /* AvenirNext-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19321C503F000E41811 /* AvenirNext-Thin.ttf */; }; D7F2F1BC21C503F000E41811 /* AvenirNext-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19321C503F000E41811 /* AvenirNext-Thin.ttf */; }; D7F2F1BD21C503F000E41811 /* AvenirNext-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19321C503F000E41811 /* AvenirNext-Thin.ttf */; }; D7F2F1BE21C503F000E41811 /* AvenirNext-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19421C503F000E41811 /* AvenirNext-Italic.ttf */; }; D7F2F1BF21C503F000E41811 /* AvenirNext-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19421C503F000E41811 /* AvenirNext-Italic.ttf */; }; D7F2F1C021C503F000E41811 /* AvenirNext-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19421C503F000E41811 /* AvenirNext-Italic.ttf */; }; D7F2F1C121C503F000E41811 /* AvenirNext-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19521C503F000E41811 /* AvenirNext-LightItalic.ttf */; }; D7F2F1C221C503F000E41811 /* AvenirNext-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19521C503F000E41811 /* AvenirNext-LightItalic.ttf */; }; D7F2F1C321C503F000E41811 /* AvenirNext-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19521C503F000E41811 /* AvenirNext-LightItalic.ttf */; }; D7F2F1C421C503F000E41811 /* AvenirNext-UltraLightIt.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19621C503F000E41811 /* AvenirNext-UltraLightIt.ttf */; }; D7F2F1C521C503F000E41811 /* AvenirNext-UltraLightIt.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19621C503F000E41811 /* AvenirNext-UltraLightIt.ttf */; }; D7F2F1C621C503F000E41811 /* AvenirNext-UltraLightIt.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7F2F19621C503F000E41811 /* AvenirNext-UltraLightIt.ttf */; }; D7F5C0EF223ED0570038F172 /* PreferencesGeneralViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F5C0EE223ED0570038F172 /* PreferencesGeneralViewController.swift */; }; D7F5C0F0223ED0570038F172 /* PreferencesGeneralViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F5C0EE223ED0570038F172 /* PreferencesGeneralViewController.swift */; }; D7F5C0F2223ED0C00038F172 /* PreferencesUserInterfaceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F5C0F1223ED0C00038F172 /* PreferencesUserInterfaceViewController.swift */; }; D7F5C0F3223ED0C00038F172 /* PreferencesUserInterfaceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F5C0F1223ED0C00038F172 /* PreferencesUserInterfaceViewController.swift */; }; D7F5C0F5223ED5620038F172 /* PreferencesEditorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F5C0F4223ED5620038F172 /* PreferencesEditorViewController.swift */; }; D7F5C0F6223ED5620038F172 /* PreferencesEditorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F5C0F4223ED5620038F172 /* PreferencesEditorViewController.swift */; }; D7F5C0F8223ED57D0038F172 /* PreferencesSecurityViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F5C0F7223ED57D0038F172 /* PreferencesSecurityViewController.swift */; }; D7F5C0F9223ED57D0038F172 /* PreferencesSecurityViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F5C0F7223ED57D0038F172 /* PreferencesSecurityViewController.swift */; }; D7F5C0FB223ED58F0038F172 /* PreferencesAdvancedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F5C0FA223ED58F0038F172 /* PreferencesAdvancedViewController.swift */; }; D7F5C0FC223ED58F0038F172 /* PreferencesAdvancedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F5C0FA223ED58F0038F172 /* PreferencesAdvancedViewController.swift */; }; D7F6CFF02056AC22008C584A /* LanguageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F6CFEF2056AC22008C584A /* LanguageViewController.swift */; }; D7F95F3A1FF2759300E2A447 /* NotesTextProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F95F391FF2759300E2A447 /* NotesTextProcessor.swift */; }; D7FA916520555067002BB0AB /* SourceCodePro-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7DD79561F4E606600D5724B /* SourceCodePro-Black.ttf */; }; D7FA916620555067002BB0AB /* SourceCodePro-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7CD5F671F508E6A006AA35D /* SourceCodePro-Bold.ttf */; }; D7FA916720555067002BB0AB /* SourceCodePro-BoldIt.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7CD5F661F508E6A006AA35D /* SourceCodePro-BoldIt.ttf */; }; D7FA916820555067002BB0AB /* SourceCodePro-It.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7DD79551F4E606600D5724B /* SourceCodePro-It.ttf */; }; D7FA916920555067002BB0AB /* SourceCodePro-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7DD795A1F4E611200D5724B /* SourceCodePro-Regular.ttf */; }; D7FB716C2BE7F66500808E56 /* EditorViewController+QuickLook.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FB716B2BE7F66500808E56 /* EditorViewController+QuickLook.swift */; }; D7FDA4F7236DBC6900C3B4AA /* Sidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FDA4F6236DBC6900C3B4AA /* Sidebar.swift */; }; D7FFD09C1FF677ED0064CBA6 /* NotesTextProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F95F391FF2759300E2A447 /* NotesTextProcessor.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ D75F333E205EC34800CC887E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D7793C671F211C6000CA39B7 /* Project object */; proxyType = 1; remoteGlobalIDString = D75F3335205EC34800CC887E; remoteInfo = "FSNotes iOS Share"; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 6F13BB1A20FEDDF50005E120 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; 6F13BB6620FEDE560005E120 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; D713542C2042D46E00E3776F /* Embed Watch Content */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; dstSubfolderSpec = 16; files = ( ); name = "Embed Watch Content"; runOnlyForDeploymentPostprocessing = 0; }; D721893F21020D3300FE3AF2 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; D73E3DE0205D17360044FF84 /* Embed App Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 13; files = ( D75F3340205EC34800CC887E /* FSNotes iOS Share Extension.appex in Embed App Extensions */, ); name = "Embed App Extensions"; runOnlyForDeploymentPostprocessing = 0; }; D7D7CD56232791670016AC15 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 6; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; D7D7CD58232791810016AC15 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 7; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 1102DDB02EE4C277005029A6 /* EditTextView+Complete.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EditTextView+Complete.swift"; sourceTree = ""; }; 110BE0102EE86B4600C5E456 /* Clojure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clojure.swift; sourceTree = ""; }; 110BE0142EE8C1C600C5E456 /* Html.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Html.swift; sourceTree = ""; }; 110BE0182EE8C24B00C5E456 /* Css.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Css.swift; sourceTree = ""; }; 110BE01C2EE8C3BA00C5E456 /* Shell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = ""; }; 110BE0202EE8C53600C5E456 /* TypeScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeScript.swift; sourceTree = ""; }; 110BE0242EE8C5AC00C5E456 /* Lisp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lisp.swift; sourceTree = ""; }; 110D09812E9C1525001555FA /* NSRange+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSRange+.swift"; sourceTree = ""; }; 110E409D2EA0150000C62F49 /* NSTextCheckingResult+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextCheckingResult+.swift"; sourceTree = ""; }; 110E40A32EA039AA00C62F49 /* EditTextView+DragOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EditTextView+DragOperation.swift"; sourceTree = ""; }; 111013142EC8F1B600B6CF1B /* ImagePreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePreviewViewController.swift; sourceTree = ""; }; 113685592EC7A2130033767F /* NSMutableAttributedString+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSMutableAttributedString+.swift"; sourceTree = ""; }; 1136855B2EC7A69C0033767F /* Platform.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Platform.swift; sourceTree = ""; }; 113685612EC869860033767F /* URL+Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Image.swift"; sourceTree = ""; }; 113685672EC889DC0033767F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 113685692EC8AE260033767F /* UIBarButtonItem+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+.swift"; sourceTree = ""; }; 113A319F2EEE2D3A009B50B0 /* Note+Preview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Note+Preview.swift"; sourceTree = ""; }; 11598DA22EDCB8D40036E387 /* UIFont+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+.swift"; sourceTree = ""; }; 116182862E62B046005B5EE0 /* SwiftHighlighter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftHighlighter.swift; sourceTree = ""; }; 1166D1F62E91BA7F00B061CA /* CodeBlockDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeBlockDetector.swift; sourceTree = ""; }; 1175E0912EDC929400B92794 /* ny-2026.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = "ny-2026.icon"; sourceTree = ""; }; 1175E0992EDCA60200B92794 /* FSNotes - Readme.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "FSNotes - Readme.md"; sourceTree = ""; }; 1175E09A2EDCA60200B92794 /* FSNotes 4.0 Change Log.textbundle */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = "FSNotes 4.0 Change Log.textbundle"; sourceTree = ""; }; 1175E09B2EDCA60200B92794 /* FSNotes 4.0 for iOS.textbundle */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = "FSNotes 4.0 for iOS.textbundle"; sourceTree = ""; }; 1175E09C2EDCA60200B92794 /* FSNotes 5.0 Change Log.textbundle */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = "FSNotes 5.0 Change Log.textbundle"; sourceTree = ""; }; 1175E09D2EDCA60200B92794 /* Meet FSNotes 6.textbundle */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = "Meet FSNotes 6.textbundle"; sourceTree = ""; }; 117C0E4C2EEDB9B70086419C /* EditTextView+Clicked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EditTextView+Clicked.swift"; sourceTree = ""; }; 11A6A8F92EF06B90005D000A /* EditTextView+MoveLines.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EditTextView+MoveLines.swift"; sourceTree = ""; }; 11A6A8FC2EF074D2005D000A /* EditTextView+Todo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EditTextView+Todo.swift"; sourceTree = ""; }; 11A95B612EDC56DC0081ED29 /* modern.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = modern.icon; sourceTree = ""; }; 11AA4B1E2EF9A4680075A9E4 /* EditorViewController+ScrollPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EditorViewController+ScrollPosition.swift"; sourceTree = ""; }; 11ABE5E12EEEFCF700E7C9EB /* NotesCounterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesCounterView.swift; sourceTree = ""; }; 11AF633D2E898430004E7157 /* Sql.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sql.swift; sourceTree = ""; }; 11B3F5952F182E4E00A3531D /* EditorViewController+Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EditorViewController+Search.swift"; sourceTree = ""; }; 11BD71652EDC87B700541BF9 /* classic-2025.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = "classic-2025.icon"; sourceTree = ""; }; 11BD8F912EDDEC33000673A7 /* GitHubDark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitHubDark.swift; sourceTree = ""; }; 11BD8F952EDDF320000673A7 /* SolarizedLight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SolarizedLight.swift; sourceTree = ""; }; 11BD8F992EDDF332000673A7 /* SolarizedDark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SolarizedDark.swift; sourceTree = ""; }; 11BD8F9D2EDE022C000673A7 /* AtomOneLight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtomOneLight.swift; sourceTree = ""; }; 11BD8FA12EDE023E000673A7 /* AtomOneDark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtomOneDark.swift; sourceTree = ""; }; 11BD8FA52EDE0679000673A7 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; 11BF06692EE331B2006C7336 /* Scala.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scala.swift; sourceTree = ""; }; 11BF066D2EE331FE006C7336 /* Bash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bash.swift; sourceTree = ""; }; 11BF06712EE3325F006C7336 /* Haskell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Haskell.swift; sourceTree = ""; }; 11BF06752EE49542006C7336 /* Lua.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lua.swift; sourceTree = ""; }; 11BF06792EE495C0006C7336 /* Perl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Perl.swift; sourceTree = ""; }; 11BF067D2EE49689006C7336 /* Erlang.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Erlang.swift; sourceTree = ""; }; 11D6C0C62EE22567006017F0 /* Python.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Python.swift; sourceTree = ""; }; 11D6C0CA2EE225E1006017F0 /* C.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = C.swift; sourceTree = ""; }; 11D6C0CE2EE22799006017F0 /* Cpp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cpp.swift; sourceTree = ""; }; 11D6C0D22EE227F6006017F0 /* Java.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Java.swift; sourceTree = ""; }; 11D6C0D62EE229E7006017F0 /* Go.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Go.swift; sourceTree = ""; }; 11D6C0DA2EE22B0D006017F0 /* Rust.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Rust.swift; sourceTree = ""; }; 11D6C0DE2EE22B74006017F0 /* Csharp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Csharp.swift; sourceTree = ""; }; 11D6C0E22EE22BEE006017F0 /* Kotlin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Kotlin.swift; sourceTree = ""; }; 11D6C0E62EE22C0F006017F0 /* R.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = R.swift; sourceTree = ""; }; 11D6C0EA2EE22C69006017F0 /* Ruby.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ruby.swift; sourceTree = ""; }; 11D6C0EE2EE22CB8006017F0 /* Matlab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Matlab.swift; sourceTree = ""; }; 11D6C0F22EE22D39006017F0 /* Dart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dart.swift; sourceTree = ""; }; 11D6C0F62EE22D73006017F0 /* Vb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vb.swift; sourceTree = ""; }; 11D6C0FA2EE22DFC006017F0 /* Assembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Assembly.swift; sourceTree = ""; }; 11D6C0FE2EE22E48006017F0 /* Scratch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scratch.swift; sourceTree = ""; }; 11D6C1022EE22E8B006017F0 /* Groovy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Groovy.swift; sourceTree = ""; }; 11D6C1062EE22EE8006017F0 /* ObjectiveC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectiveC.swift; sourceTree = ""; }; 11D702A52E5ADDE2004DBAEC /* LayoutManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutManager.swift; sourceTree = ""; }; 11D702AB2E5B8E02004DBAEC /* HtmlExtractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HtmlExtractor.swift; sourceTree = ""; }; 11D943192E643EF20010CC2B /* JavaScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JavaScript.swift; sourceTree = ""; }; 11D9431D2E643F1F0010CC2B /* Swift.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Swift.swift; sourceTree = ""; }; 11D943212E643F3B0010CC2B /* Php.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Php.swift; sourceTree = ""; }; 11D943262E643F5E0010CC2B /* GitHubLight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitHubLight.swift; sourceTree = ""; }; 11F018AB2EF7E77B00F07580 /* MPreviewFindPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPreviewFindPanel.swift; sourceTree = ""; }; 11F018B12EF8415200F07580 /* MPreviewContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPreviewContainerView.swift; sourceTree = ""; }; 11F177192EF1E92C00CC566F /* ViewController+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController+Menu.swift"; sourceTree = ""; }; 11F2D4F32F1042F4002E4E47 /* Project+Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Project+Date.swift"; sourceTree = ""; }; 11F389552EEA108C0008EC18 /* Mermaid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mermaid.swift; sourceTree = ""; }; 11F43F3A2F127C6900652350 /* Meet FSNotes 7.textbundle */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = "Meet FSNotes 7.textbundle"; sourceTree = ""; }; 11F5D53A2EFDA17000A66466 /* modern.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = modern.icon; sourceTree = ""; }; 15136766333878E10A6B0457 /* Pods_FSNotes__Notarized_.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FSNotes__Notarized_.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 275592961F3AE9B5006B8988 /* MainWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindowController.swift; sourceTree = ""; }; 2799407B218484C900727B20 /* TitleBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleBarView.swift; sourceTree = ""; }; 365935BA74CEA7B5CAFAB536 /* Pods-FSNotes.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSNotes.debug.xcconfig"; path = "Target Support Files/Pods-FSNotes/Pods-FSNotes.debug.xcconfig"; sourceTree = ""; }; 42E001C52ADAC2930099E7AD /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; 42E001C92ADAC2930099E7AD /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; 42E001CB2ADAC2930099E7AD /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = ""; }; 42E001CD2ADAC2930099E7AD /* mul */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = mul; path = mul.lproj/Main.xcstrings; sourceTree = ""; }; 42E001CE2ADAC2930099E7AD /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; 4796D64E77383E6A5A3F900B /* Pods-FSNotes iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSNotes iOS.release.xcconfig"; path = "Target Support Files/Pods-FSNotes iOS/Pods-FSNotes iOS.release.xcconfig"; sourceTree = ""; }; 484D580095FCD450AE46554D /* Pods-FSNotes iOS Share Extension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSNotes iOS Share Extension.debug.xcconfig"; path = "Target Support Files/Pods-FSNotes iOS Share Extension/Pods-FSNotes iOS Share Extension.debug.xcconfig"; sourceTree = ""; }; 6066605B9BAF43A4BF3B60C1 /* Pods_FSNotes__iCloud_.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FSNotes__iCloud_.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 62E7ACC8B47FFD05898BD354 /* Pods-FSNotes (iCloud).debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSNotes (iCloud).debug.xcconfig"; path = "Target Support Files/Pods-FSNotes (iCloud)/Pods-FSNotes (iCloud).debug.xcconfig"; sourceTree = ""; }; 6ED8B562C4824136E3D33EDC /* Pods-FSNotesCore macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSNotesCore macOS.debug.xcconfig"; path = "Target Support Files/Pods-FSNotesCore macOS/Pods-FSNotesCore macOS.debug.xcconfig"; sourceTree = ""; }; 6F13BB2820FEDE230005E120 /* DateFormatter+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+.swift"; sourceTree = ""; }; 6F13BB2920FEDE230005E120 /* String+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = ""; }; 6F13BB2A20FEDE230005E120 /* URL+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+.swift"; sourceTree = ""; }; 6F13BB3820FEDE230005E120 /* UserDataService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDataService.swift; sourceTree = ""; }; 747C0F2A42B8190F5AC0ECDB /* Pods-FSNotes (iCloud Documents).debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSNotes (iCloud Documents).debug.xcconfig"; path = "Target Support Files/Pods-FSNotes (iCloud Documents)/Pods-FSNotes (iCloud Documents).debug.xcconfig"; sourceTree = ""; }; 781ADC8297B1AC61D8E278F6 /* Pods-FSNotes iOS Share Extension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSNotes iOS Share Extension.release.xcconfig"; path = "Target Support Files/Pods-FSNotes iOS Share Extension/Pods-FSNotes iOS Share Extension.release.xcconfig"; sourceTree = ""; }; 85B72F46887638CA9CC70D39 /* Pods_FSNotes_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FSNotes_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8F7136ED23490CBF004DFA6E /* Markdown.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Markdown.swift; sourceTree = ""; }; 9381D32FA909CAB6102C4A5C /* Pods-FSNotes (Notarized).debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSNotes (Notarized).debug.xcconfig"; path = "Target Support Files/Pods-FSNotes (Notarized)/Pods-FSNotes (Notarized).debug.xcconfig"; sourceTree = ""; }; 99068B82274CF88F23C4761D /* Pods_FSNotes_iOS_Share_Extension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FSNotes_iOS_Share_Extension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9EA62EEDB6BE9BF8727E66E0 /* Pods-FSNotes.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSNotes.release.xcconfig"; path = "Target Support Files/Pods-FSNotes/Pods-FSNotes.release.xcconfig"; sourceTree = ""; }; A0C1E679E6D0B408CAC6372D /* Pods-FSNotesCore macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSNotesCore macOS.release.xcconfig"; path = "Target Support Files/Pods-FSNotesCore macOS/Pods-FSNotesCore macOS.release.xcconfig"; sourceTree = ""; }; B3A8E91DD978BBA557F778F9 /* Pods_FSNotesCore_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FSNotesCore_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C1E2B9BF60C418804784CC3B /* Pods-FSNotes (Notarized).release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSNotes (Notarized).release.xcconfig"; path = "Target Support Files/Pods-FSNotes (Notarized)/Pods-FSNotes (Notarized).release.xcconfig"; sourceTree = ""; }; C8E92DD65B370BD263427B83 /* Pods-FSNotes (iCloud Documents).release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSNotes (iCloud Documents).release.xcconfig"; path = "Target Support Files/Pods-FSNotes (iCloud Documents)/Pods-FSNotes (iCloud Documents).release.xcconfig"; sourceTree = ""; }; D7013DFF26C3B116006F58E3 /* NSColor+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSColor+.swift"; sourceTree = ""; }; D7038E2520FB24E000A54E69 /* NoteAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteAttachment.swift; sourceTree = ""; }; D7063969202230BB00BC8446 /* EditorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorViewController.swift; sourceTree = ""; }; D70716DB2307E82900B44B0D /* SingleImageTouchDownGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleImageTouchDownGestureRecognizer.swift; sourceTree = ""; }; D708AC662000EF5800A1760F /* NoteType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteType.swift; sourceTree = ""; }; D709C9E129AFD9E0006EF9A8 /* GitTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitTableViewCell.swift; sourceTree = ""; }; D70B1FAA29213EDF003923DC /* HyperlinkTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HyperlinkTextField.swift; sourceTree = ""; }; D7104A63230BD8C500B6D8EE /* SortDirection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SortDirection.swift; sourceTree = ""; }; D71354032042AFC800E3776F /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; D714496120C72D3400D7AD46 /* UIImage+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+.swift"; sourceTree = ""; }; D714749A279CE8EE001A8B29 /* MainNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainNavigationController.swift; sourceTree = ""; }; D714749C279D7DBC001A8B29 /* SearchQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchQuery.swift; sourceTree = ""; }; D7153DFC2285A93300A2C20F /* AboutWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutWindowController.swift; sourceTree = ""; }; D7153E042285C09C00A2C20F /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; D7153E082285EC6100A2C20F /* TitleTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleTextField.swift; sourceTree = ""; }; D7163D2E24E81B5C00B1FC05 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; D7163D3324E81D9900B1FC05 /* MainInterface.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MainInterface.storyboard; sourceTree = ""; }; D7170C1C20F8565B001DDB36 /* FileSystemEventManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystemEventManager.swift; sourceTree = ""; }; D71AA0212143A4A8004AFD2A /* MoveViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveViewController.swift; sourceTree = ""; }; D71B9D792867027000D2F323 /* NoteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteViewController.swift; sourceTree = ""; }; D71B9D812868658100D2F323 /* EditorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorViewController.swift; sourceTree = ""; }; D71B9D852868BF7F00D2F323 /* TextStorageProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextStorageProcessor.swift; sourceTree = ""; }; D71C4A4D1F520F0E00EBA30B /* MPreview.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = MPreview.bundle; sourceTree = ""; }; D71FD2242101CFD0008BEFA1 /* UITextView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+.swift"; sourceTree = ""; }; D720240922A9412B000A7691 /* Markdown.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Markdown.icns; sourceTree = ""; }; D720240A22A9412B000A7691 /* Text.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Text.icns; sourceTree = ""; }; D720240B22A9412B000A7691 /* TextBundle.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = TextBundle.icns; sourceTree = ""; }; D720241822A941A3000A7691 /* EncryptedTextPack.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = EncryptedTextPack.icns; sourceTree = ""; }; D72682A929BE8E1F00F6E961 /* RepositoryAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepositoryAction.swift; sourceTree = ""; }; D726DE89287ACC1E00F8406C /* NSWindow+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWindow+.swift"; sourceTree = ""; }; D72DAF0729B27D75001243BB /* ProjectSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectSettings.swift; sourceTree = ""; }; D72E05861F5220D300977D02 /* Down.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Down.framework; sourceTree = ""; }; D730829123084340003185D1 /* MPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPreviewView.swift; sourceTree = ""; }; D730BD26222BF30700E69C93 /* KeychainConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainConfiguration.swift; sourceTree = ""; }; D730BD29222BF32A00E69C93 /* KeychainPasswordItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainPasswordItem.swift; sourceTree = ""; }; D730BD2D222DABA100E69C93 /* NoteContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteContainer.swift; sourceTree = ""; }; D730BD34222DB11E00E69C93 /* TextBundleInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextBundleInfo.swift; sourceTree = ""; }; D730BD3B222DB9FC00E69C93 /* NameHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NameHelper.swift; sourceTree = ""; }; D730BD59223BFEB200E69C93 /* RuntimeError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeError.swift; sourceTree = ""; }; D7315ECE215ECF3000AB49D4 /* EditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorView.swift; sourceTree = ""; }; D7315ED1215ED15500AB49D4 /* SidebarSplitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarSplitView.swift; sourceTree = ""; }; D7315ED4215ED95600AB49D4 /* NSAppearance+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAppearance+.swift"; sourceTree = ""; }; D735E5BC1F2EF66000173215 /* NoteCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteCellView.swift; sourceTree = ""; }; D735E5BE1F2F001500173215 /* NoteRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteRowView.swift; sourceTree = ""; }; D73673A720D10CF2000BA61D /* CloudDriveManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudDriveManager.swift; sourceTree = ""; }; D736DDA827B5DD370012ED70 /* EditorSelectionRect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorSelectionRect.swift; sourceTree = ""; }; D736DDAA27BABFF80012ED70 /* RevisionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RevisionsViewController.swift; sourceTree = ""; }; D736DDAC27BAC7940012ED70 /* Note+History.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Note+History.swift"; sourceTree = ""; }; D73794C023366F5200E75A28 /* ImageScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageScrollView.swift; sourceTree = ""; }; D738356C2242871400B260DD /* MasterPasswordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterPasswordViewController.swift; sourceTree = ""; }; D73B3134298FBF4400F46144 /* GitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitViewController.swift; sourceTree = ""; }; D73BCC5428EB5EBE008B3BBC /* Commit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Commit.swift; sourceTree = ""; }; D73BCC5628EB5EBE008B3BBC /* Branch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Branch.swift; sourceTree = ""; }; D73BCC5728EB5EBE008B3BBC /* Branches.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Branches.swift; sourceTree = ""; }; D73BCC5828EB5EBE008B3BBC /* BranchesIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BranchesIterator.swift; sourceTree = ""; }; D73BCC5A28EB5EBE008B3BBC /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; D73BCC5B28EB5EBE008B3BBC /* ConfigManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigManager.swift; sourceTree = ""; }; D73BCC5C28EB5EBE008B3BBC /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; D73BCC5D28EB5EBE008B3BBC /* Wrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Wrapper.swift; sourceTree = ""; }; D73BCC5E28EB5EBE008B3BBC /* Progress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Progress.swift; sourceTree = ""; }; D73BCC5F28EB5EBE008B3BBC /* Blob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Blob.swift; sourceTree = ""; }; D73BCC6028EB5EBF008B3BBC /* OID.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OID.swift; sourceTree = ""; }; D73BCC6128EB5EBF008B3BBC /* Signature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signature.swift; sourceTree = ""; }; D73BCC6228EB5EBF008B3BBC /* Object.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Object.swift; sourceTree = ""; }; D73BCC6428EB5EBF008B3BBC /* TagIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TagIterator.swift; sourceTree = ""; }; D73BCC6528EB5EBF008B3BBC /* Tags.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tags.swift; sourceTree = ""; }; D73BCC6628EB5EBF008B3BBC /* Tag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tag.swift; sourceTree = ""; }; D73BCC6828EB5EBF008B3BBC /* RevisionIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RevisionIterator.swift; sourceTree = ""; }; D73BCC6928EB5EBF008B3BBC /* FileHistoryIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileHistoryIterator.swift; sourceTree = ""; }; D73BCC6B28EB5EC0008B3BBC /* DiffEntry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiffEntry.swift; sourceTree = ""; }; D73BCC6C28EB5EC0008B3BBC /* Diff.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Diff.swift; sourceTree = ""; }; D73BCC6E28EB5EC0008B3BBC /* KeyAuthentication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyAuthentication.swift; sourceTree = ""; }; D73BCC6F28EB5EC0008B3BBC /* Authentication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Authentication.swift; sourceTree = ""; }; D73BCC7028EB5EC0008B3BBC /* PasswordAuthentication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordAuthentication.swift; sourceTree = ""; }; D73BCC7228EB5EC0008B3BBC /* Remote.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Remote.swift; sourceTree = ""; }; D73BCC7328EB5EC0008B3BBC /* Remotes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Remotes.swift; sourceTree = ""; }; D73BCC7528EB5EC1008B3BBC /* Statuses.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Statuses.swift; sourceTree = ""; }; D73BCC7628EB5EC1008B3BBC /* Status.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = ""; }; D73BCC7728EB5EC1008B3BBC /* StatusIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusIterator.swift; sourceTree = ""; }; D73BCC7928EB5EC1008B3BBC /* Index.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Index.swift; sourceTree = ""; }; D73BCC7A28EB5EC1008B3BBC /* Index+Commit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Index+Commit.swift"; sourceTree = ""; }; D73BCC7B28EB5EC1008B3BBC /* Index+Files.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Index+Files.swift"; sourceTree = ""; }; D73BCC7D28EB5EC2008B3BBC /* Head+Checkout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Head+Checkout.swift"; sourceTree = ""; }; D73BCC7E28EB5EC2008B3BBC /* Head.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Head.swift; sourceTree = ""; }; D73BCC7F28EB5EC2008B3BBC /* Head+Merge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Head+Merge.swift"; sourceTree = ""; }; D73BCC8128EB5EC2008B3BBC /* TreeEntry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TreeEntry.swift; sourceTree = ""; }; D73BCC8228EB5EC2008B3BBC /* Tree.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tree.swift; sourceTree = ""; }; D73BCC8428EB5EC3008B3BBC /* RepositoryManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepositoryManager.swift; sourceTree = ""; }; D73BCC8528EB5EC3008B3BBC /* Repository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repository.swift; sourceTree = ""; }; D73BCC8628EB5EC3008B3BBC /* Repository+Open.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Repository+Open.swift"; sourceTree = ""; }; D73BCC8728EB5EC3008B3BBC /* Repository+Lookup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Repository+Lookup.swift"; sourceTree = ""; }; D73BCC8828EB5EC3008B3BBC /* Repository+Commit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Repository+Commit.swift"; sourceTree = ""; }; D73BCC8A28EB5EC3008B3BBC /* Reference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reference.swift; sourceTree = ""; }; D73BCC8B28EB5EC3008B3BBC /* Reference+Target.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Reference+Target.swift"; sourceTree = ""; }; D73FAE9E21553CAA0058BE61 /* UIApplication+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+.swift"; sourceTree = ""; }; D74112271FABA21B00AB619A /* MainWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = ""; }; D7465F27207F2CD600E46A52 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; D7470D062170E890006B2A92 /* NSTextStorage++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextStorage++.swift"; sourceTree = ""; }; D7487FD5217389F800D09383 /* NSImage+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSImage+.swift"; sourceTree = ""; }; D7487FE82174E5CB00D09383 /* NSAttributedStringKey+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAttributedStringKey+.swift"; sourceTree = ""; }; D74922111FACE9B100C45108 /* Down.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Down.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D74B7B642137D3A1007F5331 /* AttributedBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedBox.swift; sourceTree = ""; }; D74B7B682137EFA9007F5331 /* SingleTouchDownGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleTouchDownGestureRecognizer.swift; sourceTree = ""; }; D74D479E256DF2EB00D97647 /* FSNTextAttachmentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FSNTextAttachmentCell.swift; sourceTree = ""; }; D7508FC71F337E850047AB76 /* SearchTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTextField.swift; sourceTree = ""; }; D7508FCD1F3438540047AB76 /* PrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsViewController.swift; sourceTree = ""; }; D752D80723454750006842F9 /* NSTextAttachment+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextAttachment+.swift"; sourceTree = ""; }; D75627CD26D1165A000AF6EA /* ImageFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageFormat.swift; sourceTree = ""; }; D75629B027D4DB7E00F55588 /* CodeFontViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeFontViewController.swift; sourceTree = ""; }; D75629B227D4DE9F00F55588 /* CodeThemeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeThemeViewController.swift; sourceTree = ""; }; D75629B427D5036D00F55588 /* SortByViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SortByViewController.swift; sourceTree = ""; }; D75629B627D53EB100F55588 /* ThanksViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThanksViewController.swift; sourceTree = ""; }; D75A34E427D7CD440085438F /* SidebarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarViewController.swift; sourceTree = ""; }; D75EE7F62078B22D0055F159 /* SidebarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarItem.swift; sourceTree = ""; }; D75EE7F92078B3C00055F159 /* Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar.swift; sourceTree = ""; }; D75EE7FC2078C5460055F159 /* SidebarCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarCellView.swift; sourceTree = ""; }; D75EE7FF2078E0C60055F159 /* SidebarItemType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarItemType.swift; sourceTree = ""; }; D75F3336205EC34800CC887E /* FSNotes iOS Share Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "FSNotes iOS Share Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; D75F3338205EC34800CC887E /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; D75F333D205EC34800CC887E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D75F3344205ECE8900CC887E /* FSNotes iOS Share.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "FSNotes iOS Share.entitlements"; sourceTree = ""; }; D76025B1204EEF64000B9F59 /* TextFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFormatter.swift; sourceTree = ""; }; D76447DB1F3A4F0700965F01 /* UserDefaultsManagement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsManagement.swift; sourceTree = ""; }; D7679375201F0BFD000F7BBF /* SortBy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SortBy.swift; sourceTree = ""; }; D7679386201F21F5000F7BBF /* FSNotes iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "FSNotes iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; D7679388201F21F5000F7BBF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; D767938A201F21F5000F7BBF /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; D7679397201F21F5000F7BBF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D7680FB125D02B2C00810DA8 /* FileManager+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+.swift"; sourceTree = ""; }; D768D754245E854D0028F344 /* NSAttributedString+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+.swift"; sourceTree = ""; }; D768D75B245ED6470028F344 /* VerticallyAlignedTextFieldCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerticallyAlignedTextFieldCell.swift; sourceTree = ""; }; D76E10C0215A55CE0017F4A3 /* Date+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+.swift"; sourceTree = ""; }; D77015812C972B6D00CFF0E8 /* SettingsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTableViewCell.swift; sourceTree = ""; }; D771E96E28EDFBF600CD4871 /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; D772C8832217362C007E440B /* ViewController+Print.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController+Print.swift"; sourceTree = ""; }; D773DE7F1F36F45900A39C9F /* SandboxBookmark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SandboxBookmark.swift; sourceTree = ""; }; D77671B71FACEDDE00DDF28D /* MASShortcut.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MASShortcut.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D777D7802009115C00D86B33 /* ImagesProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagesProcessor.swift; sourceTree = ""; }; D7793C6F1F211C6000CA39B7 /* FSNotes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FSNotes.app; sourceTree = BUILT_PRODUCTS_DIR; }; D7793C721F211C6000CA39B7 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; D7793C741F211C6000CA39B7 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; D7793C791F211C6000CA39B7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; D7793C7C1F211C6000CA39B7 /* FSNotes.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FSNotes.entitlements; sourceTree = ""; }; D779C7BA1F415BE300FADEE1 /* PrefsWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsWindowController.swift; sourceTree = ""; }; D77A6F7E28B11496006A0353 /* PreferencesWebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWebViewController.swift; sourceTree = ""; }; D77AD7FB27F9D1C90077BD45 /* Data+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+.swift"; sourceTree = ""; }; D77CC040216A608500582B97 /* EditorScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorScrollView.swift; sourceTree = ""; }; D77E05282463124300AD7772 /* StorageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageType.swift; sourceTree = ""; }; D77F41B22A0D48F500E2B7A2 /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; D77F89DE28D38B5D00BECC87 /* ViewController+Web.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController+Web.swift"; sourceTree = ""; }; D78115622153B36C004FA1CA /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.swift; sourceTree = ""; }; D78115662153D4D9004FA1CA /* ProjectsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectsViewController.swift; sourceTree = ""; }; D781156A2153E05A004FA1CA /* ProjectSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectSettingsViewController.swift; sourceTree = ""; }; D783B504208A1BFD00328A41 /* EditorSplitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorSplitView.swift; sourceTree = ""; }; D785805B27A3483B000C1BAF /* FolderPopoverActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderPopoverActions.swift; sourceTree = ""; }; D78678CA2093AE10001A6620 /* UndoData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UndoData.swift; sourceTree = ""; }; D78D20D41F934C05006DBBC3 /* FSNotes (CloudKit).entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = "FSNotes (CloudKit).entitlements"; sourceTree = ""; }; D792297421A845B4005F468F /* ProjectSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectSettingsViewController.swift; sourceTree = ""; }; D792DD8127A6C980006ADC01 /* FSParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FSParser.swift; sourceTree = ""; }; D792DD9527A6D71C006ADC01 /* String+Punycode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Punycode.swift"; sourceTree = ""; }; D794558D27C05743000C283F /* ProViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProViewController.swift; sourceTree = ""; }; D794559927C1B3F9000C283F /* ExternalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalViewController.swift; sourceTree = ""; }; D7958A3822ED512D00EDBDDC /* SandboxBookmark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SandboxBookmark.swift; sourceTree = ""; }; D79651B02517741400333AD4 /* ProgressState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressState.swift; sourceTree = ""; }; D796EB40251E127300CE5C80 /* Pasteboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pasteboard.swift; sourceTree = ""; }; D797004B1F3DD10700BAD94D /* EditTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTextView.swift; sourceTree = ""; }; D79798A129C0FE6A00B9A878 /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; D79A13CC2A0E9C980037510B /* UIColor+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+.swift"; sourceTree = ""; }; D79C26242872384C00CB70E6 /* EditorViewController+Sharing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EditorViewController+Sharing.swift"; sourceTree = ""; }; D79FE8A01F77D04A00113CFD /* Note.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Note.swift; sourceTree = ""; }; D7A415131F2FBDA00099B82C /* NotesTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesTableView.swift; sourceTree = ""; }; D7A549C224DD9D3400537544 /* SettingsEditorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsEditorViewController.swift; sourceTree = ""; }; D7A65C5820F11C38003E5ADC /* LanguageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageType.swift; sourceTree = ""; }; D7A9C1D52910784400905619 /* Project+Git.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Project+Git.swift"; sourceTree = ""; }; D7ADFD102066CF9400B531F9 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.2.sdk/System/Library/Frameworks/CoreLocation.framework; sourceTree = DEVELOPER_DIR; }; D7B13DBC2C64F445008EBCAA /* Printer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Printer.swift; sourceTree = ""; }; D7B34F9625195D7E0007877E /* PreviewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewState.swift; sourceTree = ""; }; D7B4AC5D2471253100F3888A /* NoteMeta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteMeta.swift; sourceTree = ""; }; D7B6E599207912E300FE0E20 /* Project.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Project.swift; sourceTree = ""; }; D7BAC62E249D11F8008D29AA /* SettingsFilesNaming.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFilesNaming.swift; sourceTree = ""; }; D7BB2DFD29A0157700D5055A /* Storage+Git.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Git.swift"; sourceTree = ""; }; D7BCF034296B0DAA00F72A4F /* AboutImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutImageView.swift; sourceTree = ""; }; D7BDFE66201F679B00897A58 /* FSNotes iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "FSNotes iOS.entitlements"; sourceTree = ""; }; D7BDFE69201F68B700897A58 /* NotesTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesTableView.swift; sourceTree = ""; }; D7BDFE6B201F6DC200897A58 /* EditTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTextView.swift; sourceTree = ""; }; D7BDFE6F201F788D00897A58 /* NoteCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteCellView.swift; sourceTree = ""; }; D7C1C999235606CB0021A32D /* SidebarHeaderCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarHeaderCellView.swift; sourceTree = ""; }; D7C33F6D29E09A690006C473 /* AppIconViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconViewController.swift; sourceTree = ""; }; D7C6DB5A25AA880600F8F76F /* ViewController+More.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController+More.swift"; sourceTree = ""; }; D7C803ED2046DBBD005DA599 /* DefaultExtensionControllerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultExtensionControllerView.swift; sourceTree = ""; }; D7C9029123547A1E00A89BD8 /* FSTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FSTag.swift; sourceTree = ""; }; D7CA7FD3232652E300E9717A /* PreferencesGitViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesGitViewController.swift; sourceTree = ""; }; D7CB9904207E5AE300037E91 /* SidebarTableRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarTableRowView.swift; sourceTree = ""; }; D7CBAFFD214D5A1C002ECD5A /* ShortcutIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutIdentifier.swift; sourceTree = ""; }; D7CC44C02A1E5E4F00743857 /* ViewController+WebApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController+WebApi.swift"; sourceTree = ""; }; D7CC44C52A1E5F7600743857 /* ApiResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiResponse.swift; sourceTree = ""; }; D7CCEDB82C6BA2F300A3BB83 /* ClickableTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClickableTextField.swift; sourceTree = ""; }; D7CD5CCA21820A380009D63B /* UserDefaultsManagement+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaultsManagement+.swift"; sourceTree = ""; }; D7CD5CDA21832C190009D63B /* UserDefaultsManagement+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaultsManagement+.swift"; sourceTree = ""; }; D7CD5F661F508E6A006AA35D /* SourceCodePro-BoldIt.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-BoldIt.ttf"; sourceTree = ""; }; D7CD5F671F508E6A006AA35D /* SourceCodePro-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Bold.ttf"; sourceTree = ""; }; D7CD5F6B1F51185F006AA35D /* NSFont+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSFont+.swift"; sourceTree = ""; }; D7CDE9DB2161767A00DC5978 /* AppearanceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceType.swift; sourceTree = ""; }; D7CE196B1FA4BA5E004BF8EE /* PreviewTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewTextField.swift; sourceTree = ""; }; D7CF7EAA29E2093C00FEC0C5 /* SecurityViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecurityViewController.swift; sourceTree = ""; }; D7D01AFC2C65203A00F545D0 /* PrinterLegacy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrinterLegacy.swift; sourceTree = ""; }; D7D03BAE205C250500D96A6D /* FontViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontViewController.swift; sourceTree = ""; }; D7D1DE67216D05A800AC1845 /* NameTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NameTextField.swift; sourceTree = ""; }; D7D372F3207B5B0F00AFBD9F /* SidebarNotesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarNotesView.swift; sourceTree = ""; }; D7D372F6207BB09500AFBD9F /* SidebarOutlineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarOutlineView.swift; sourceTree = ""; }; D7D79C27236798C300898A2D /* Welcome.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Welcome.bundle; sourceTree = ""; }; D7D7CD39232774BC0016AC15 /* ViewController+Git.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController+Git.swift"; sourceTree = ""; }; D7D9503C209D806F001FB60B /* SidebarTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarTableView.swift; sourceTree = ""; }; D7D9503E209D846E001FB60B /* SidebarTableCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarTableCellView.swift; sourceTree = ""; }; D7D97F3B290437A200C651D4 /* NSWindowController+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWindowController+.swift"; sourceTree = ""; }; D7DA9E1D21031901001CF0BE /* OutlineHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutlineHeaderView.swift; sourceTree = ""; }; D7DA9E2021033489001CF0BE /* NSMutableAttributedString+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSMutableAttributedString+.swift"; sourceTree = ""; }; D7DB5ED520248D5500E7E1B6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; D7DD5A871F88D4EA00CE947E /* FileWatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileWatcher.swift; sourceTree = ""; }; D7DD5A891F88D50000CE947E /* FileWatcherEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileWatcherEvent.swift; sourceTree = ""; }; D7DD79551F4E606600D5724B /* SourceCodePro-It.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-It.ttf"; sourceTree = ""; }; D7DD79561F4E606600D5724B /* SourceCodePro-Black.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Black.ttf"; sourceTree = ""; }; D7DD795A1F4E611200D5724B /* SourceCodePro-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Regular.ttf"; sourceTree = ""; }; D7E025071F3B6DDB00EDDA32 /* Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = ""; }; D7E32C2B28F8D0740048614B /* StaticSshKeyDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticSshKeyDelegate.swift; sourceTree = ""; }; D7E51712220D814D00A9CAD9 /* UTI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTI.swift; sourceTree = ""; }; D7E6ACE820832D40003599A2 /* AppDelegate+URLRoutes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AppDelegate+URLRoutes.swift"; sourceTree = ""; }; D7E7DB3227A9B16F00408725 /* DatePickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickerViewController.swift; sourceTree = ""; }; D7E81C5A1F925B5F00416A91 /* FSNotes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FSNotes.app; sourceTree = BUILT_PRODUCTS_DIR; }; D7EDEDFA219203C9000B8C1A /* NoteCellView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NoteCellView+.swift"; sourceTree = ""; }; D7F2F18721C503EF00E41811 /* AvenirNext-MediumItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-MediumItalic.ttf"; sourceTree = ""; }; D7F2F18821C503EF00E41811 /* AvenirNext-UltraLight.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-UltraLight.ttf"; sourceTree = ""; }; D7F2F18921C503EF00E41811 /* AvenirNext-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-Medium.ttf"; sourceTree = ""; }; D7F2F18A21C503EF00E41811 /* AvenirNext-Heavy.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-Heavy.ttf"; sourceTree = ""; }; D7F2F18B21C503EF00E41811 /* AvenirNext-DemiItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-DemiItalic.ttf"; sourceTree = ""; }; D7F2F18C21C503EF00E41811 /* AvenirNext-BoldItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-BoldItalic.ttf"; sourceTree = ""; }; D7F2F18D21C503EF00E41811 /* AvenirNext-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-Regular.ttf"; sourceTree = ""; }; D7F2F18E21C503EF00E41811 /* AvenirNext-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-Light.ttf"; sourceTree = ""; }; D7F2F18F21C503EF00E41811 /* AvenirNext-Demi.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-Demi.ttf"; sourceTree = ""; }; D7F2F19021C503EF00E41811 /* AvenirNext-HeavyItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-HeavyItalic.ttf"; sourceTree = ""; }; D7F2F19121C503F000E41811 /* AvenirNext-ThinItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-ThinItalic.ttf"; sourceTree = ""; }; D7F2F19221C503F000E41811 /* AvenirNext-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-Bold.ttf"; sourceTree = ""; }; D7F2F19321C503F000E41811 /* AvenirNext-Thin.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-Thin.ttf"; sourceTree = ""; }; D7F2F19421C503F000E41811 /* AvenirNext-Italic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-Italic.ttf"; sourceTree = ""; }; D7F2F19521C503F000E41811 /* AvenirNext-LightItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-LightItalic.ttf"; sourceTree = ""; }; D7F2F19621C503F000E41811 /* AvenirNext-UltraLightIt.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AvenirNext-UltraLightIt.ttf"; sourceTree = ""; }; D7F5C0EE223ED0570038F172 /* PreferencesGeneralViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesGeneralViewController.swift; sourceTree = ""; }; D7F5C0F1223ED0C00038F172 /* PreferencesUserInterfaceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesUserInterfaceViewController.swift; sourceTree = ""; }; D7F5C0F4223ED5620038F172 /* PreferencesEditorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesEditorViewController.swift; sourceTree = ""; }; D7F5C0F7223ED57D0038F172 /* PreferencesSecurityViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesSecurityViewController.swift; sourceTree = ""; }; D7F5C0FA223ED58F0038F172 /* PreferencesAdvancedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesAdvancedViewController.swift; sourceTree = ""; }; D7F6CFEF2056AC22008C584A /* LanguageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageViewController.swift; sourceTree = ""; }; D7F95F391FF2759300E2A447 /* NotesTextProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesTextProcessor.swift; sourceTree = ""; }; D7FB716B2BE7F66500808E56 /* EditorViewController+QuickLook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EditorViewController+QuickLook.swift"; sourceTree = ""; }; D7FDA4F6236DBC6900C3B4AA /* Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar.swift; sourceTree = ""; }; E73B6900CD2E14B0D5E51599 /* Pods-FSNotes iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSNotes iOS.debug.xcconfig"; path = "Target Support Files/Pods-FSNotes iOS/Pods-FSNotes iOS.debug.xcconfig"; sourceTree = ""; }; EF024607824ECBF0AE378D65 /* Pods-FSNotes (iCloud).release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSNotes (iCloud).release.xcconfig"; path = "Target Support Files/Pods-FSNotes (iCloud)/Pods-FSNotes (iCloud).release.xcconfig"; sourceTree = ""; }; F616385FF783029192B97DF6 /* Pods_FSNotes.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FSNotes.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ D75F3333205EC34800CC887E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 48BEA1E16CCED6900AD756F7 /* Pods_FSNotes_iOS_Share_Extension.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; D7679383201F21F5000F7BBF /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( D7A9C1DB29107A0800905619 /* Git in Frameworks */, D7D2F2802B54BDD4003DCA47 /* Shout in Frameworks */, D7ADFD112066CF9400B531F9 /* CoreLocation.framework in Frameworks */, CE3427A778205E1713A014B9 /* Pods_FSNotes_iOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; D7793C6C1F211C6000CA39B7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( D7D2F2822B54BE59003DCA47 /* Shout in Frameworks */, D70F830628CE8596004818C5 /* Git in Frameworks */, D4DB932C9F51CAE71393A28B /* Pods_FSNotes.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; D7E81C471F925B5F00416A91 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( D7D2F27E2B54BD42003DCA47 /* Shout in Frameworks */, D70F830428CE858E004818C5 /* Git in Frameworks */, BE957A4A1B908EC91BECB3D3 /* Pods_FSNotes__iCloud_.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 111013172EC9B46800B6CF1B /* SourceCodePro */ = { isa = PBXGroup; children = ( D7DD79561F4E606600D5724B /* SourceCodePro-Black.ttf */, D7CD5F671F508E6A006AA35D /* SourceCodePro-Bold.ttf */, D7CD5F661F508E6A006AA35D /* SourceCodePro-BoldIt.ttf */, D7DD79551F4E606600D5724B /* SourceCodePro-It.ttf */, D7DD795A1F4E611200D5724B /* SourceCodePro-Regular.ttf */, ); path = SourceCodePro; sourceTree = ""; }; 11598D9F2EDCB85F0036E387 /* Helpers */ = { isa = PBXGroup; children = ( D7CBAFFD214D5A1C002ECD5A /* ShortcutIdentifier.swift */, D78115622153B36C004FA1CA /* Buttons.swift */, D7FDA4F6236DBC6900C3B4AA /* Sidebar.swift */, D785805B27A3483B000C1BAF /* FolderPopoverActions.swift */, D74B7B682137EFA9007F5331 /* SingleTouchDownGestureRecognizer.swift */, D70716DB2307E82900B44B0D /* SingleImageTouchDownGestureRecognizer.swift */, D73673A720D10CF2000BA61D /* CloudDriveManager.swift */, D7958A3822ED512D00EDBDDC /* SandboxBookmark.swift */, ); path = Helpers; sourceTree = ""; }; 1175E09E2EDCA60200B92794 /* Initial */ = { isa = PBXGroup; children = ( 1175E0992EDCA60200B92794 /* FSNotes - Readme.md */, 1175E09A2EDCA60200B92794 /* FSNotes 4.0 Change Log.textbundle */, 1175E09B2EDCA60200B92794 /* FSNotes 4.0 for iOS.textbundle */, 1175E09C2EDCA60200B92794 /* FSNotes 5.0 Change Log.textbundle */, 1175E09D2EDCA60200B92794 /* Meet FSNotes 6.textbundle */, 11F43F3A2F127C6900652350 /* Meet FSNotes 7.textbundle */, ); path = Initial; sourceTree = ""; }; 11C23F022EDC486E0064C5B5 /* Icons */ = { isa = PBXGroup; children = ( 11A95B612EDC56DC0081ED29 /* modern.icon */, 11BD71652EDC87B700541BF9 /* classic-2025.icon */, 1175E0912EDC929400B92794 /* ny-2026.icon */, ); path = Icons; sourceTree = ""; }; 11D943172E643ECF0010CC2B /* SwiftHighlighter */ = { isa = PBXGroup; children = ( 11D943252E643F520010CC2B /* Themes */, 11D943182E643EE40010CC2B /* Languages */, 116182862E62B046005B5EE0 /* SwiftHighlighter.swift */, 11BD8FA52EDE0679000673A7 /* Theme.swift */, 1136855B2EC7A69C0033767F /* Platform.swift */, ); path = SwiftHighlighter; sourceTree = ""; }; 11D943182E643EE40010CC2B /* Languages */ = { isa = PBXGroup; children = ( 11F389552EEA108C0008EC18 /* Mermaid.swift */, 110BE0242EE8C5AC00C5E456 /* Lisp.swift */, 110BE0202EE8C53600C5E456 /* TypeScript.swift */, 110BE01C2EE8C3BA00C5E456 /* Shell.swift */, 110BE0182EE8C24B00C5E456 /* Css.swift */, 110BE0142EE8C1C600C5E456 /* Html.swift */, 110BE0102EE86B4600C5E456 /* Clojure.swift */, 11BF067D2EE49689006C7336 /* Erlang.swift */, 11BF06792EE495C0006C7336 /* Perl.swift */, 11BF06752EE49542006C7336 /* Lua.swift */, 11BF06712EE3325F006C7336 /* Haskell.swift */, 11BF066D2EE331FE006C7336 /* Bash.swift */, 11BF06692EE331B2006C7336 /* Scala.swift */, 11D6C1062EE22EE8006017F0 /* ObjectiveC.swift */, 11D6C1022EE22E8B006017F0 /* Groovy.swift */, 11D6C0FE2EE22E48006017F0 /* Scratch.swift */, 11D6C0FA2EE22DFC006017F0 /* Assembly.swift */, 11D6C0F62EE22D73006017F0 /* Vb.swift */, 11D6C0F22EE22D39006017F0 /* Dart.swift */, 11D6C0EE2EE22CB8006017F0 /* Matlab.swift */, 11D6C0EA2EE22C69006017F0 /* Ruby.swift */, 11D6C0E62EE22C0F006017F0 /* R.swift */, 11D6C0E22EE22BEE006017F0 /* Kotlin.swift */, 11D6C0DE2EE22B74006017F0 /* Csharp.swift */, 11D6C0DA2EE22B0D006017F0 /* Rust.swift */, 11D6C0D62EE229E7006017F0 /* Go.swift */, 11D6C0D22EE227F6006017F0 /* Java.swift */, 11D6C0CE2EE22799006017F0 /* Cpp.swift */, 11D6C0CA2EE225E1006017F0 /* C.swift */, 11D6C0C62EE22567006017F0 /* Python.swift */, 11AF633D2E898430004E7157 /* Sql.swift */, 11D943212E643F3B0010CC2B /* Php.swift */, 11D9431D2E643F1F0010CC2B /* Swift.swift */, 11D943192E643EF20010CC2B /* JavaScript.swift */, ); path = Languages; sourceTree = ""; }; 11D943252E643F520010CC2B /* Themes */ = { isa = PBXGroup; children = ( 11BD8FA12EDE023E000673A7 /* AtomOneDark.swift */, 11BD8F9D2EDE022C000673A7 /* AtomOneLight.swift */, 11BD8F992EDDF332000673A7 /* SolarizedDark.swift */, 11BD8F952EDDF320000673A7 /* SolarizedLight.swift */, 11BD8F912EDDEC33000673A7 /* GitHubDark.swift */, 11D943262E643F5E0010CC2B /* GitHubLight.swift */, ); path = Themes; sourceTree = ""; }; 6F13BB2320FEDE230005E120 /* FSNotesCore */ = { isa = PBXGroup; children = ( D7C52B8028C7A8C2001B2065 /* Git */, 11D943172E643ECF0010CC2B /* SwiftHighlighter */, D7218944210211B800FE3AF2 /* Business */, 6F13BB2720FEDE230005E120 /* Extensions */, 1166D1F62E91BA7F00B061CA /* CodeBlockDetector.swift */, D7470D062170E890006B2A92 /* NSTextStorage++.swift */, D730BD26222BF30700E69C93 /* KeychainConfiguration.swift */, D730BD29222BF32A00E69C93 /* KeychainPasswordItem.swift */, D730BD3B222DB9FC00E69C93 /* NameHelper.swift */, D752D80723454750006842F9 /* NSTextAttachment+.swift */, D7EDEDFA219203C9000B8C1A /* NoteCellView+.swift */, D7B4AC5D2471253100F3888A /* NoteMeta.swift */, D792DD8127A6C980006ADC01 /* FSParser.swift */, D736DDAC27BAC7940012ED70 /* Note+History.swift */, D71B9D852868BF7F00D2F323 /* TextStorageProcessor.swift */, D7F95F391FF2759300E2A447 /* NotesTextProcessor.swift */, D777D7802009115C00D86B33 /* ImagesProcessor.swift */, D72682A929BE8E1F00F6E961 /* RepositoryAction.swift */, D7CC44C02A1E5E4F00743857 /* ViewController+WebApi.swift */, 11D702AB2E5B8E02004DBAEC /* HtmlExtractor.swift */, D76447DB1F3A4F0700965F01 /* UserDefaultsManagement.swift */, D76025B1204EEF64000B9F59 /* TextFormatter.swift */, D730829123084340003185D1 /* MPreviewView.swift */, ); path = FSNotesCore; sourceTree = ""; }; 6F13BB2720FEDE230005E120 /* Extensions */ = { isa = PBXGroup; children = ( 113685612EC869860033767F /* URL+Image.swift */, 110E409D2EA0150000C62F49 /* NSTextCheckingResult+.swift */, 110D09812E9C1525001555FA /* NSRange+.swift */, 6F13BB2820FEDE230005E120 /* DateFormatter+.swift */, 6F13BB2920FEDE230005E120 /* String+.swift */, 6F13BB2A20FEDE230005E120 /* URL+.swift */, D7DA9E2021033489001CF0BE /* NSMutableAttributedString+.swift */, D76E10C0215A55CE0017F4A3 /* Date+.swift */, D7487FE82174E5CB00D09383 /* NSAttributedStringKey+.swift */, D7E51712220D814D00A9CAD9 /* UTI.swift */, D768D754245E854D0028F344 /* NSAttributedString+.swift */, D796EB40251E127300CE5C80 /* Pasteboard.swift */, D7680FB125D02B2C00810DA8 /* FileManager+.swift */, D792DD9527A6D71C006ADC01 /* String+Punycode.swift */, D77AD7FB27F9D1C90077BD45 /* Data+.swift */, D7A9C1D52910784400905619 /* Project+Git.swift */, D7BB2DFD29A0157700D5055A /* Storage+Git.swift */, ); path = Extensions; sourceTree = ""; }; C5F4774EAA923C4003B182F8 /* Frameworks */ = { isa = PBXGroup; children = ( D7ADFD102066CF9400B531F9 /* CoreLocation.framework */, D77671B71FACEDDE00DDF28D /* MASShortcut.framework */, D74922111FACE9B100C45108 /* Down.framework */, D72E05861F5220D300977D02 /* Down.framework */, F616385FF783029192B97DF6 /* Pods_FSNotes.framework */, 15136766333878E10A6B0457 /* Pods_FSNotes__Notarized_.framework */, 85B72F46887638CA9CC70D39 /* Pods_FSNotes_iOS.framework */, 99068B82274CF88F23C4761D /* Pods_FSNotes_iOS_Share_Extension.framework */, B3A8E91DD978BBA557F778F9 /* Pods_FSNotesCore_macOS.framework */, 6066605B9BAF43A4BF3B60C1 /* Pods_FSNotes__iCloud_.framework */, ); name = Frameworks; sourceTree = ""; }; D720240722A9410D000A7691 /* Icons */ = { isa = PBXGroup; children = ( D720240922A9412B000A7691 /* Markdown.icns */, D720240A22A9412B000A7691 /* Text.icns */, D720240B22A9412B000A7691 /* TextBundle.icns */, D720241822A941A3000A7691 /* EncryptedTextPack.icns */, ); path = Icons; sourceTree = ""; }; D7218944210211B800FE3AF2 /* Business */ = { isa = PBXGroup; children = ( D7B6E599207912E300FE0E20 /* Project.swift */, 11F2D4F32F1042F4002E4E47 /* Project+Date.swift */, D79FE8A01F77D04A00113CFD /* Note.swift */, 113A319F2EEE2D3A009B50B0 /* Note+Preview.swift */, D714749C279D7DBC001A8B29 /* SearchQuery.swift */, D7BAC62E249D11F8008D29AA /* SettingsFilesNaming.swift */, D7038E2520FB24E000A54E69 /* NoteAttachment.swift */, D74B7B642137D3A1007F5331 /* AttributedBox.swift */, D730BD2D222DABA100E69C93 /* NoteContainer.swift */, D730BD34222DB11E00E69C93 /* TextBundleInfo.swift */, D730BD59223BFEB200E69C93 /* RuntimeError.swift */, D7679375201F0BFD000F7BBF /* SortBy.swift */, D7104A63230BD8C500B6D8EE /* SortDirection.swift */, D7C9029123547A1E00A89BD8 /* FSTag.swift */, D77E05282463124300AD7772 /* StorageType.swift */, D7B34F9625195D7E0007877E /* PreviewState.swift */, D79651B02517741400333AD4 /* ProgressState.swift */, D75627CD26D1165A000AF6EA /* ImageFormat.swift */, D7CC44C52A1E5F7600743857 /* ApiResponse.swift */, D7E025071F3B6DDB00EDDA32 /* Storage.swift */, D75EE7FF2078E0C60055F159 /* SidebarItemType.swift */, D708AC662000EF5800A1760F /* NoteType.swift */, D78678CA2093AE10001A6620 /* UndoData.swift */, D7A65C5820F11C38003E5ADC /* LanguageType.swift */, D7CDE9DB2161767A00DC5978 /* AppearanceType.swift */, D72DAF0729B27D75001243BB /* ProjectSettings.swift */, 8F7136ED23490CBF004DFA6E /* Markdown.swift */, D75EE7F62078B22D0055F159 /* SidebarItem.swift */, ); path = Business; sourceTree = ""; }; D73BCC5328EB5EBE008B3BBC /* commit */ = { isa = PBXGroup; children = ( D73BCC5428EB5EBE008B3BBC /* Commit.swift */, ); path = commit; sourceTree = ""; }; D73BCC5528EB5EBE008B3BBC /* branch */ = { isa = PBXGroup; children = ( D73BCC5628EB5EBE008B3BBC /* Branch.swift */, D73BCC5728EB5EBE008B3BBC /* Branches.swift */, D73BCC5828EB5EBE008B3BBC /* BranchesIterator.swift */, ); path = branch; sourceTree = ""; }; D73BCC5928EB5EBE008B3BBC /* commons */ = { isa = PBXGroup; children = ( D771E96E28EDFBF600CD4871 /* Errors.swift */, D73BCC5A28EB5EBE008B3BBC /* Strings.swift */, D73BCC5B28EB5EBE008B3BBC /* ConfigManager.swift */, D73BCC5C28EB5EBE008B3BBC /* Error.swift */, D73BCC5D28EB5EBE008B3BBC /* Wrapper.swift */, D73BCC5E28EB5EBE008B3BBC /* Progress.swift */, D73BCC5F28EB5EBE008B3BBC /* Blob.swift */, D73BCC6028EB5EBF008B3BBC /* OID.swift */, D73BCC6128EB5EBF008B3BBC /* Signature.swift */, D73BCC6228EB5EBF008B3BBC /* Object.swift */, D7E32C2B28F8D0740048614B /* StaticSshKeyDelegate.swift */, ); path = commons; sourceTree = ""; }; D73BCC6328EB5EBF008B3BBC /* tag */ = { isa = PBXGroup; children = ( D73BCC6428EB5EBF008B3BBC /* TagIterator.swift */, D73BCC6528EB5EBF008B3BBC /* Tags.swift */, D73BCC6628EB5EBF008B3BBC /* Tag.swift */, ); path = tag; sourceTree = ""; }; D73BCC6728EB5EBF008B3BBC /* revision */ = { isa = PBXGroup; children = ( D73BCC6828EB5EBF008B3BBC /* RevisionIterator.swift */, D73BCC6928EB5EBF008B3BBC /* FileHistoryIterator.swift */, ); path = revision; sourceTree = ""; }; D73BCC6A28EB5EC0008B3BBC /* diff */ = { isa = PBXGroup; children = ( D73BCC6B28EB5EC0008B3BBC /* DiffEntry.swift */, D73BCC6C28EB5EC0008B3BBC /* Diff.swift */, ); path = diff; sourceTree = ""; }; D73BCC6D28EB5EC0008B3BBC /* authentication */ = { isa = PBXGroup; children = ( D73BCC6E28EB5EC0008B3BBC /* KeyAuthentication.swift */, D73BCC6F28EB5EC0008B3BBC /* Authentication.swift */, D73BCC7028EB5EC0008B3BBC /* PasswordAuthentication.swift */, ); path = authentication; sourceTree = ""; }; D73BCC7128EB5EC0008B3BBC /* remote */ = { isa = PBXGroup; children = ( D73BCC7228EB5EC0008B3BBC /* Remote.swift */, D73BCC7328EB5EC0008B3BBC /* Remotes.swift */, ); path = remote; sourceTree = ""; }; D73BCC7428EB5EC1008B3BBC /* status */ = { isa = PBXGroup; children = ( D73BCC7528EB5EC1008B3BBC /* Statuses.swift */, D73BCC7628EB5EC1008B3BBC /* Status.swift */, D73BCC7728EB5EC1008B3BBC /* StatusIterator.swift */, ); path = status; sourceTree = ""; }; D73BCC7828EB5EC1008B3BBC /* index */ = { isa = PBXGroup; children = ( D73BCC7928EB5EC1008B3BBC /* Index.swift */, D73BCC7A28EB5EC1008B3BBC /* Index+Commit.swift */, D73BCC7B28EB5EC1008B3BBC /* Index+Files.swift */, ); path = index; sourceTree = ""; }; D73BCC7C28EB5EC2008B3BBC /* head */ = { isa = PBXGroup; children = ( D73BCC7D28EB5EC2008B3BBC /* Head+Checkout.swift */, D73BCC7E28EB5EC2008B3BBC /* Head.swift */, D73BCC7F28EB5EC2008B3BBC /* Head+Merge.swift */, ); path = head; sourceTree = ""; }; D73BCC8028EB5EC2008B3BBC /* tree */ = { isa = PBXGroup; children = ( D73BCC8128EB5EC2008B3BBC /* TreeEntry.swift */, D73BCC8228EB5EC2008B3BBC /* Tree.swift */, ); path = tree; sourceTree = ""; }; D73BCC8328EB5EC3008B3BBC /* repository */ = { isa = PBXGroup; children = ( D73BCC8428EB5EC3008B3BBC /* RepositoryManager.swift */, D73BCC8528EB5EC3008B3BBC /* Repository.swift */, D73BCC8628EB5EC3008B3BBC /* Repository+Open.swift */, D73BCC8728EB5EC3008B3BBC /* Repository+Lookup.swift */, D73BCC8828EB5EC3008B3BBC /* Repository+Commit.swift */, ); path = repository; sourceTree = ""; }; D73BCC8928EB5EC3008B3BBC /* reference */ = { isa = PBXGroup; children = ( D73BCC8A28EB5EC3008B3BBC /* Reference.swift */, D73BCC8B28EB5EC3008B3BBC /* Reference+Target.swift */, ); path = reference; sourceTree = ""; }; D75C903C223EF727002A9CAD /* Preferences */ = { isa = PBXGroup; children = ( D7F5C0EE223ED0570038F172 /* PreferencesGeneralViewController.swift */, D7F5C0F1223ED0C00038F172 /* PreferencesUserInterfaceViewController.swift */, D7F5C0F4223ED5620038F172 /* PreferencesEditorViewController.swift */, D7F5C0F7223ED57D0038F172 /* PreferencesSecurityViewController.swift */, D7F5C0FA223ED58F0038F172 /* PreferencesAdvancedViewController.swift */, D738356C2242871400B260DD /* MasterPasswordViewController.swift */, D7CA7FD3232652E300E9717A /* PreferencesGitViewController.swift */, D77A6F7E28B11496006A0353 /* PreferencesWebViewController.swift */, D79798A129C0FE6A00B9A878 /* SettingsViewController.swift */, ); path = Preferences; sourceTree = ""; }; D75F3337205EC34800CC887E /* FSNotes iOS Share */ = { isa = PBXGroup; children = ( D75F3344205ECE8900CC887E /* FSNotes iOS Share.entitlements */, D75F3338205EC34800CC887E /* ShareViewController.swift */, 113685592EC7A2130033767F /* NSMutableAttributedString+.swift */, D75F333D205EC34800CC887E /* Info.plist */, D7163D3324E81D9900B1FC05 /* MainInterface.storyboard */, 42E001C92ADAC2930099E7AD /* Localizable.xcstrings */, ); path = "FSNotes iOS Share"; sourceTree = ""; }; D7679387201F21F5000F7BBF /* FSNotes iOS */ = { isa = PBXGroup; children = ( 11C23F022EDC486E0064C5B5 /* Icons */, D7F6CFEE2056ABDB008C584A /* Preferences */, D7ED3FFC20CD0ADE001438EE /* Extensions */, 11598D9F2EDCB85F0036E387 /* Helpers */, D7ED3FFB20CD0AA3001438EE /* View */, D7BDFE66201F679B00897A58 /* FSNotes iOS.entitlements */, D7679388201F21F5000F7BBF /* AppDelegate.swift */, 113685672EC889DC0033767F /* SceneDelegate.swift */, 111013142EC8F1B600B6CF1B /* ImagePreviewViewController.swift */, D7E7DB3227A9B16F00408725 /* DatePickerViewController.swift */, D71AA0212143A4A8004AFD2A /* MoveViewController.swift */, D736DDAA27BABFF80012ED70 /* RevisionsViewController.swift */, D714749A279CE8EE001A8B29 /* MainNavigationController.swift */, D767938A201F21F5000F7BBF /* ViewController.swift */, D7C6DB5A25AA880600F8F76F /* ViewController+More.swift */, D7063969202230BB00BC8446 /* EditorViewController.swift */, 11B3F5952F182E4E00A3531D /* EditorViewController+Search.swift */, D7FB716B2BE7F66500808E56 /* EditorViewController+QuickLook.swift */, D7163D2E24E81B5C00B1FC05 /* Main.storyboard */, D77F41B22A0D48F500E2B7A2 /* Launch Screen.storyboard */, D7679397201F21F5000F7BBF /* Info.plist */, 42E001CB2ADAC2930099E7AD /* InfoPlist.xcstrings */, 42E001CE2ADAC2930099E7AD /* Localizable.xcstrings */, D7DB5ED520248D5500E7E1B6 /* Assets.xcassets */, ); path = "FSNotes iOS"; sourceTree = ""; }; D7793C661F211C6000CA39B7 = { isa = PBXGroup; children = ( D7D61FCC1F32EEA1004357C2 /* Resources */, 6F13BB2320FEDE230005E120 /* FSNotesCore */, D7793C711F211C6000CA39B7 /* FSNotes */, D7679387201F21F5000F7BBF /* FSNotes iOS */, D75F3337205EC34800CC887E /* FSNotes iOS Share */, D7793C701F211C6000CA39B7 /* Products */, C5F4774EAA923C4003B182F8 /* Frameworks */, FA35FD3BB467B0413F2F0A8A /* Pods */, ); sourceTree = ""; }; D7793C701F211C6000CA39B7 /* Products */ = { isa = PBXGroup; children = ( D7793C6F1F211C6000CA39B7 /* FSNotes.app */, D7E81C5A1F925B5F00416A91 /* FSNotes.app */, D7679386201F21F5000F7BBF /* FSNotes iOS.app */, D75F3336205EC34800CC887E /* FSNotes iOS Share Extension.appex */, ); name = Products; sourceTree = ""; }; D7793C711F211C6000CA39B7 /* FSNotes */ = { isa = PBXGroup; children = ( D75C903C223EF727002A9CAD /* Preferences */, D7CD5F6A1F51184A006AA35D /* Extensions */, D7E6D9D520808652003ECAFC /* Helpers */, D7E6D9D2208085BB003ECAFC /* View */, D7793C721F211C6000CA39B7 /* AppDelegate.swift */, 11D702A52E5ADDE2004DBAEC /* LayoutManager.swift */, D7E6ACE820832D40003599A2 /* AppDelegate+URLRoutes.swift */, D74112271FABA21B00AB619A /* MainWindow.swift */, 275592961F3AE9B5006B8988 /* MainWindowController.swift */, D7793C741F211C6000CA39B7 /* ViewController.swift */, D772C8832217362C007E440B /* ViewController+Print.swift */, D7D7CD39232774BC0016AC15 /* ViewController+Git.swift */, D77F89DE28D38B5D00BECC87 /* ViewController+Web.swift */, 11F177192EF1E92C00CC566F /* ViewController+Menu.swift */, D792297421A845B4005F468F /* ProjectSettingsViewController.swift */, D779C7BA1F415BE300FADEE1 /* PrefsWindowController.swift */, D7508FCD1F3438540047AB76 /* PrefsViewController.swift */, D7153DFC2285A93300A2C20F /* AboutWindowController.swift */, D7153E042285C09C00A2C20F /* AboutViewController.swift */, D71B9D792867027000D2F323 /* NoteViewController.swift */, D71B9D812868658100D2F323 /* EditorViewController.swift */, 11AA4B1E2EF9A4680075A9E4 /* EditorViewController+ScrollPosition.swift */, D79C26242872384C00CB70E6 /* EditorViewController+Sharing.swift */, D7D97F3B290437A200C651D4 /* NSWindowController+.swift */, 42E001C52ADAC2930099E7AD /* Localizable.xcstrings */, D7793C781F211C6000CA39B7 /* Main.storyboard */, D7465F27207F2CD600E46A52 /* Images.xcassets */, D7793C7C1F211C6000CA39B7 /* FSNotes.entitlements */, 11F5D53A2EFDA17000A66466 /* modern.icon */, D78D20D41F934C05006DBBC3 /* FSNotes (CloudKit).entitlements */, ); path = FSNotes; sourceTree = ""; }; D7C52B8028C7A8C2001B2065 /* Git */ = { isa = PBXGroup; children = ( D73BCC6D28EB5EC0008B3BBC /* authentication */, D73BCC5528EB5EBE008B3BBC /* branch */, D73BCC5328EB5EBE008B3BBC /* commit */, D73BCC5928EB5EBE008B3BBC /* commons */, D73BCC6A28EB5EC0008B3BBC /* diff */, D73BCC7C28EB5EC2008B3BBC /* head */, D73BCC7828EB5EC1008B3BBC /* index */, D73BCC8928EB5EC3008B3BBC /* reference */, D73BCC7128EB5EC0008B3BBC /* remote */, D73BCC8328EB5EC3008B3BBC /* repository */, D73BCC6728EB5EBF008B3BBC /* revision */, D73BCC7428EB5EC1008B3BBC /* status */, D73BCC6328EB5EBF008B3BBC /* tag */, D73BCC8028EB5EC2008B3BBC /* tree */, ); path = Git; sourceTree = ""; }; D7CD5F6A1F51184A006AA35D /* Extensions */ = { isa = PBXGroup; children = ( D726DE89287ACC1E00F8406C /* NSWindow+.swift */, D7013DFF26C3B116006F58E3 /* NSColor+.swift */, D7487FD5217389F800D09383 /* NSImage+.swift */, D7CD5F6B1F51185F006AA35D /* NSFont+.swift */, D7315ED4215ED95600AB49D4 /* NSAppearance+.swift */, D7CD5CCA21820A380009D63B /* UserDefaultsManagement+.swift */, ); path = Extensions; sourceTree = ""; }; D7D61FCC1F32EEA1004357C2 /* Resources */ = { isa = PBXGroup; children = ( D720240722A9410D000A7691 /* Icons */, D7F2F18521C5032D00E41811 /* Fonts */, 1175E09E2EDCA60200B92794 /* Initial */, D71C4A4D1F520F0E00EBA30B /* MPreview.bundle */, D7D79C27236798C300898A2D /* Welcome.bundle */, ); path = Resources; sourceTree = ""; }; D7E6D9D2208085BB003ECAFC /* View */ = { isa = PBXGroup; children = ( 11F018B12EF8415200F07580 /* MPreviewContainerView.swift */, 11F018AB2EF7E77B00F07580 /* MPreviewFindPanel.swift */, 11ABE5E12EEEFCF700E7C9EB /* NotesCounterView.swift */, D797004B1F3DD10700BAD94D /* EditTextView.swift */, 11A6A8FC2EF074D2005D000A /* EditTextView+Todo.swift */, 117C0E4C2EEDB9B70086419C /* EditTextView+Clicked.swift */, 1102DDB02EE4C277005029A6 /* EditTextView+Complete.swift */, 110E40A32EA039AA00C62F49 /* EditTextView+DragOperation.swift */, 11A6A8F92EF06B90005D000A /* EditTextView+MoveLines.swift */, D735E5BC1F2EF66000173215 /* NoteCellView.swift */, D735E5BE1F2F001500173215 /* NoteRowView.swift */, D7A415131F2FBDA00099B82C /* NotesTableView.swift */, D7CE196B1FA4BA5E004BF8EE /* PreviewTextField.swift */, D7508FC71F337E850047AB76 /* SearchTextField.swift */, D7D372F3207B5B0F00AFBD9F /* SidebarNotesView.swift */, D7D372F6207BB09500AFBD9F /* SidebarOutlineView.swift */, D7CB9904207E5AE300037E91 /* SidebarTableRowView.swift */, D75EE7FC2078C5460055F159 /* SidebarCellView.swift */, D7C1C999235606CB0021A32D /* SidebarHeaderCellView.swift */, D783B504208A1BFD00328A41 /* EditorSplitView.swift */, D7DA9E1D21031901001CF0BE /* OutlineHeaderView.swift */, D7315ED1215ED15500AB49D4 /* SidebarSplitView.swift */, D7315ECE215ECF3000AB49D4 /* EditorView.swift */, D77CC040216A608500582B97 /* EditorScrollView.swift */, D7D1DE67216D05A800AC1845 /* NameTextField.swift */, 2799407B218484C900727B20 /* TitleBarView.swift */, D7153E082285EC6100A2C20F /* TitleTextField.swift */, D768D75B245ED6470028F344 /* VerticallyAlignedTextFieldCell.swift */, D70B1FAA29213EDF003923DC /* HyperlinkTextField.swift */, D7BCF034296B0DAA00F72A4F /* AboutImageView.swift */, D7CCEDB82C6BA2F300A3BB83 /* ClickableTextField.swift */, ); path = View; sourceTree = ""; }; D7E6D9D520808652003ECAFC /* Helpers */ = { isa = PBXGroup; children = ( D7DD5A871F88D4EA00CE947E /* FileWatcher.swift */, D7DD5A891F88D50000CE947E /* FileWatcherEvent.swift */, D7170C1C20F8565B001DDB36 /* FileSystemEventManager.swift */, D773DE7F1F36F45900A39C9F /* SandboxBookmark.swift */, D7B13DBC2C64F445008EBCAA /* Printer.swift */, D7D01AFC2C65203A00F545D0 /* PrinterLegacy.swift */, 6F13BB3820FEDE230005E120 /* UserDataService.swift */, D75EE7F92078B3C00055F159 /* Sidebar.swift */, D74D479E256DF2EB00D97647 /* FSNTextAttachmentCell.swift */, ); path = Helpers; sourceTree = ""; }; D7ED3FFB20CD0AA3001438EE /* View */ = { isa = PBXGroup; children = ( D7BDFE6B201F6DC200897A58 /* EditTextView.swift */, D7BDFE69201F68B700897A58 /* NotesTableView.swift */, D7D9503C209D806F001FB60B /* SidebarTableView.swift */, D7D9503E209D846E001FB60B /* SidebarTableCellView.swift */, D7BDFE6F201F788D00897A58 /* NoteCellView.swift */, D736DDA827B5DD370012ED70 /* EditorSelectionRect.swift */, D73794C023366F5200E75A28 /* ImageScrollView.swift */, ); path = View; sourceTree = ""; }; D7ED3FFC20CD0ADE001438EE /* Extensions */ = { isa = PBXGroup; children = ( 113685692EC8AE260033767F /* UIBarButtonItem+.swift */, D7CD5CDA21832C190009D63B /* UserDefaultsManagement+.swift */, D714496120C72D3400D7AD46 /* UIImage+.swift */, D71FD2242101CFD0008BEFA1 /* UITextView+.swift */, D73FAE9E21553CAA0058BE61 /* UIApplication+.swift */, D79A13CC2A0E9C980037510B /* UIColor+.swift */, 11598DA22EDCB8D40036E387 /* UIFont+.swift */, ); path = Extensions; sourceTree = ""; }; D7F2F18521C5032D00E41811 /* Fonts */ = { isa = PBXGroup; children = ( 111013172EC9B46800B6CF1B /* SourceCodePro */, D7F2F18621C5034100E41811 /* AvenirNext */, ); path = Fonts; sourceTree = ""; }; D7F2F18621C5034100E41811 /* AvenirNext */ = { isa = PBXGroup; children = ( D7F2F19221C503F000E41811 /* AvenirNext-Bold.ttf */, D7F2F18C21C503EF00E41811 /* AvenirNext-BoldItalic.ttf */, D7F2F18F21C503EF00E41811 /* AvenirNext-Demi.ttf */, D7F2F18B21C503EF00E41811 /* AvenirNext-DemiItalic.ttf */, D7F2F18A21C503EF00E41811 /* AvenirNext-Heavy.ttf */, D7F2F19021C503EF00E41811 /* AvenirNext-HeavyItalic.ttf */, D7F2F19421C503F000E41811 /* AvenirNext-Italic.ttf */, D7F2F18E21C503EF00E41811 /* AvenirNext-Light.ttf */, D7F2F19521C503F000E41811 /* AvenirNext-LightItalic.ttf */, D7F2F18921C503EF00E41811 /* AvenirNext-Medium.ttf */, D7F2F18721C503EF00E41811 /* AvenirNext-MediumItalic.ttf */, D7F2F18D21C503EF00E41811 /* AvenirNext-Regular.ttf */, D7F2F19321C503F000E41811 /* AvenirNext-Thin.ttf */, D7F2F19121C503F000E41811 /* AvenirNext-ThinItalic.ttf */, D7F2F18821C503EF00E41811 /* AvenirNext-UltraLight.ttf */, D7F2F19621C503F000E41811 /* AvenirNext-UltraLightIt.ttf */, ); path = AvenirNext; sourceTree = ""; }; D7F6CFEE2056ABDB008C584A /* Preferences */ = { isa = PBXGroup; children = ( D77015812C972B6D00CFF0E8 /* SettingsTableViewCell.swift */, D71354032042AFC800E3776F /* SettingsViewController.swift */, D7C803ED2046DBBD005DA599 /* DefaultExtensionControllerView.swift */, D7F6CFEF2056AC22008C584A /* LanguageViewController.swift */, D7D03BAE205C250500D96A6D /* FontViewController.swift */, D78115662153D4D9004FA1CA /* ProjectsViewController.swift */, D781156A2153E05A004FA1CA /* ProjectSettingsViewController.swift */, D7A549C224DD9D3400537544 /* SettingsEditorViewController.swift */, D794558D27C05743000C283F /* ProViewController.swift */, D794559927C1B3F9000C283F /* ExternalViewController.swift */, D75629B027D4DB7E00F55588 /* CodeFontViewController.swift */, D75629B227D4DE9F00F55588 /* CodeThemeViewController.swift */, D75629B427D5036D00F55588 /* SortByViewController.swift */, D75629B627D53EB100F55588 /* ThanksViewController.swift */, D75A34E427D7CD440085438F /* SidebarViewController.swift */, D73B3134298FBF4400F46144 /* GitViewController.swift */, D709C9E129AFD9E0006EF9A8 /* GitTableViewCell.swift */, D7C33F6D29E09A690006C473 /* AppIconViewController.swift */, D7CF7EAA29E2093C00FEC0C5 /* SecurityViewController.swift */, ); path = Preferences; sourceTree = ""; }; FA35FD3BB467B0413F2F0A8A /* Pods */ = { isa = PBXGroup; children = ( 365935BA74CEA7B5CAFAB536 /* Pods-FSNotes.debug.xcconfig */, 9EA62EEDB6BE9BF8727E66E0 /* Pods-FSNotes.release.xcconfig */, 9381D32FA909CAB6102C4A5C /* Pods-FSNotes (Notarized).debug.xcconfig */, C1E2B9BF60C418804784CC3B /* Pods-FSNotes (Notarized).release.xcconfig */, 747C0F2A42B8190F5AC0ECDB /* Pods-FSNotes (iCloud Documents).debug.xcconfig */, C8E92DD65B370BD263427B83 /* Pods-FSNotes (iCloud Documents).release.xcconfig */, E73B6900CD2E14B0D5E51599 /* Pods-FSNotes iOS.debug.xcconfig */, 4796D64E77383E6A5A3F900B /* Pods-FSNotes iOS.release.xcconfig */, 484D580095FCD450AE46554D /* Pods-FSNotes iOS Share Extension.debug.xcconfig */, 781ADC8297B1AC61D8E278F6 /* Pods-FSNotes iOS Share Extension.release.xcconfig */, 6ED8B562C4824136E3D33EDC /* Pods-FSNotesCore macOS.debug.xcconfig */, A0C1E679E6D0B408CAC6372D /* Pods-FSNotesCore macOS.release.xcconfig */, 62E7ACC8B47FFD05898BD354 /* Pods-FSNotes (iCloud).debug.xcconfig */, EF024607824ECBF0AE378D65 /* Pods-FSNotes (iCloud).release.xcconfig */, ); path = Pods; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ D75F3335205EC34800CC887E /* FSNotes iOS Share Extension */ = { isa = PBXNativeTarget; buildConfigurationList = D75F3341205EC34800CC887E /* Build configuration list for PBXNativeTarget "FSNotes iOS Share Extension" */; buildPhases = ( F8CAD21668D14BDB8E66E536 /* [CP] Check Pods Manifest.lock */, D75F3332205EC34800CC887E /* Sources */, D75F3333205EC34800CC887E /* Frameworks */, D75F3334205EC34800CC887E /* Resources */, ); buildRules = ( ); dependencies = ( ); name = "FSNotes iOS Share Extension"; productName = "FSNotes iOS Share"; productReference = D75F3336205EC34800CC887E /* FSNotes iOS Share Extension.appex */; productType = "com.apple.product-type.app-extension"; }; D7679385201F21F5000F7BBF /* FSNotes iOS */ = { isa = PBXNativeTarget; buildConfigurationList = D7679398201F21F5000F7BBF /* Build configuration list for PBXNativeTarget "FSNotes iOS" */; buildPhases = ( D2B474F12776435E885D6144 /* [CP] Check Pods Manifest.lock */, D7679382201F21F5000F7BBF /* Sources */, D7679383201F21F5000F7BBF /* Frameworks */, D7679384201F21F5000F7BBF /* Resources */, D713542C2042D46E00E3776F /* Embed Watch Content */, D73E3DE0205D17360044FF84 /* Embed App Extensions */, 6F13BB6620FEDE560005E120 /* Embed Frameworks */, B3D3D9AF57B99BB9F7A7F798 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( D75F333F205EC34800CC887E /* PBXTargetDependency */, ); name = "FSNotes iOS"; packageProductDependencies = ( D7A9C1DA29107A0800905619 /* Git */, D7D2F27F2B54BDD4003DCA47 /* Shout */, ); productName = "FSNotes iOS"; productReference = D7679386201F21F5000F7BBF /* FSNotes iOS.app */; productType = "com.apple.product-type.application"; }; D7793C6E1F211C6000CA39B7 /* FSNotes */ = { isa = PBXNativeTarget; buildConfigurationList = D7793C951F211C6000CA39B7 /* Build configuration list for PBXNativeTarget "FSNotes" */; buildPhases = ( 17481D1DB565210A80E68547 /* [CP] Check Pods Manifest.lock */, D7793C6B1F211C6000CA39B7 /* Sources */, D7793C6C1F211C6000CA39B7 /* Frameworks */, D7793C6D1F211C6000CA39B7 /* Resources */, 6F13BB1A20FEDDF50005E120 /* Embed Frameworks */, D7D7CD58232791810016AC15 /* CopyFiles */, 064F52D71C0B9E9B54B4A092 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( D70B1FA52920DA6A003923DC /* PBXTargetDependency */, ); name = FSNotes; packageProductDependencies = ( D70F830528CE8596004818C5 /* Git */, D7D2F2812B54BE59003DCA47 /* Shout */, ); productName = FSNotes; productReference = D7793C6F1F211C6000CA39B7 /* FSNotes.app */; productType = "com.apple.product-type.application"; }; D7E81C2B1F925B5F00416A91 /* FSNotes (iCloud) */ = { isa = PBXNativeTarget; buildConfigurationList = D7E81C571F925B5F00416A91 /* Build configuration list for PBXNativeTarget "FSNotes (iCloud)" */; buildPhases = ( B7738C934BDFDCE4A24683FE /* [CP] Check Pods Manifest.lock */, D7E81C2D1F925B5F00416A91 /* Sources */, D7E81C471F925B5F00416A91 /* Frameworks */, D7E81C491F925B5F00416A91 /* Resources */, D721893F21020D3300FE3AF2 /* Embed Frameworks */, D7D7CD56232791670016AC15 /* CopyFiles */, 210B7CF3914C0A91C48CEFD3 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( D70B1FA72920DA73003923DC /* PBXTargetDependency */, ); name = "FSNotes (iCloud)"; packageProductDependencies = ( D70F830328CE858E004818C5 /* Git */, D7D2F27D2B54BD42003DCA47 /* Shout */, ); productName = FSNotes; productReference = D7E81C5A1F925B5F00416A91 /* FSNotes.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ D7793C671F211C6000CA39B7 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1000; LastUpgradeCheck = 1020; ORGANIZATIONNAME = "Oleksandr Hlushchenko"; TargetAttributes = { D75F3335205EC34800CC887E = { CreatedOnToolsVersion = 9.2; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.ApplicationGroups.iOS = { enabled = 1; }; com.apple.iCloud = { enabled = 0; }; }; }; D7679385201F21F5000F7BBF = { CreatedOnToolsVersion = 9.2; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.ApplicationGroups.iOS = { enabled = 1; }; com.apple.BackgroundModes = { enabled = 1; }; com.apple.DataProtection = { enabled = 0; }; com.apple.Push = { enabled = 1; }; com.apple.iCloud = { enabled = 1; }; }; }; D7793C6E1F211C6000CA39B7 = { CreatedOnToolsVersion = 9.0; LastSwiftMigration = 0920; ProvisioningStyle = Manual; SystemCapabilities = { com.apple.ApplicationGroups.Mac = { enabled = 0; }; com.apple.HardenedRuntime = { enabled = 0; }; com.apple.Push = { enabled = 0; }; com.apple.Sandbox = { enabled = 1; }; com.apple.iCloud = { enabled = 0; }; }; }; D7E81C2B1F925B5F00416A91 = { LastSwiftMigration = 1020; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.Push = { enabled = 1; }; com.apple.Sandbox = { enabled = 1; }; com.apple.iCloud = { enabled = 1; }; }; }; }; }; buildConfigurationList = D7793C6A1F211C6000CA39B7 /* Build configuration list for PBXProject "FSNotes" */; compatibilityVersion = "Xcode 8.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, de, es, "ar-IQ", "zh-Hans", ko, uk, fr, "nl-NL", "pt-PT", it, he, ja, "pt-BR", ru, cs, hi, tr, ); mainGroup = D7793C661F211C6000CA39B7; packageReferences = ( D7B4CC5428C7B8860046A25F /* XCRemoteSwiftPackageReference "swift-git" */, D7D2F27C2B54BD42003DCA47 /* XCRemoteSwiftPackageReference "Shout" */, ); productRefGroup = D7793C701F211C6000CA39B7 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( D7793C6E1F211C6000CA39B7 /* FSNotes */, D7E81C2B1F925B5F00416A91 /* FSNotes (iCloud) */, D7679385201F21F5000F7BBF /* FSNotes iOS */, D75F3335205EC34800CC887E /* FSNotes iOS Share Extension */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ D75F3334205EC34800CC887E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 42E001CA2ADAC2930099E7AD /* Localizable.xcstrings in Resources */, D7163D3424E81D9900B1FC05 /* MainInterface.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; D7679384201F21F5000F7BBF /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 11F43F3C2F127EE300652350 /* Meet FSNotes 7.textbundle in Resources */, D7F2F1AE21C503F000E41811 /* AvenirNext-Light.ttf in Resources */, D7F2F1B121C503F000E41811 /* AvenirNext-Demi.ttf in Resources */, D7F2F1AB21C503F000E41811 /* AvenirNext-Regular.ttf in Resources */, D7F2F19C21C503F000E41811 /* AvenirNext-UltraLight.ttf in Resources */, D75F1DF1206D660D00F70B28 /* MPreview.bundle in Resources */, D7FA916520555067002BB0AB /* SourceCodePro-Black.ttf in Resources */, D7FA916620555067002BB0AB /* SourceCodePro-Bold.ttf in Resources */, D7F2F1BD21C503F000E41811 /* AvenirNext-Thin.ttf in Resources */, D7FA916720555067002BB0AB /* SourceCodePro-BoldIt.ttf in Resources */, 11A95B622EDC56DC0081ED29 /* modern.icon in Resources */, 1175E0922EDC929400B92794 /* ny-2026.icon in Resources */, D7FA916820555067002BB0AB /* SourceCodePro-It.ttf in Resources */, D7F2F1B421C503F000E41811 /* AvenirNext-HeavyItalic.ttf in Resources */, D7FA916920555067002BB0AB /* SourceCodePro-Regular.ttf in Resources */, D7F2F19F21C503F000E41811 /* AvenirNext-Medium.ttf in Resources */, D7F2F1C621C503F000E41811 /* AvenirNext-UltraLightIt.ttf in Resources */, D7163D2F24E81B5C00B1FC05 /* Main.storyboard in Resources */, 11BD71662EDC87B700541BF9 /* classic-2025.icon in Resources */, D7F2F1C321C503F000E41811 /* AvenirNext-LightItalic.ttf in Resources */, D7F2F1C021C503F000E41811 /* AvenirNext-Italic.ttf in Resources */, D7F2F1BA21C503F000E41811 /* AvenirNext-Bold.ttf in Resources */, D7F2F1B721C503F000E41811 /* AvenirNext-ThinItalic.ttf in Resources */, 42E001CF2ADAC2930099E7AD /* Localizable.xcstrings in Resources */, D7ECE68A22B6B481006A14C6 /* TextBundle.icns in Resources */, D77F41B32A0D48F500E2B7A2 /* Launch Screen.storyboard in Resources */, D7F2F1A521C503F000E41811 /* AvenirNext-DemiItalic.ttf in Resources */, D7F2F19921C503F000E41811 /* AvenirNext-MediumItalic.ttf in Resources */, D7F2F1A821C503F000E41811 /* AvenirNext-BoldItalic.ttf in Resources */, 42E001CC2ADAC2930099E7AD /* InfoPlist.xcstrings in Resources */, 113EEBD72EDBA63D00A94F29 /* Assets.xcassets in Resources */, D7F2F1A221C503F000E41811 /* AvenirNext-Heavy.ttf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; D7793C6D1F211C6000CA39B7 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( D7F2F19A21C503F000E41811 /* AvenirNext-UltraLight.ttf in Resources */, D7F2F1BE21C503F000E41811 /* AvenirNext-Italic.ttf in Resources */, D7F2F1A021C503F000E41811 /* AvenirNext-Heavy.ttf in Resources */, D7F2F1A921C503F000E41811 /* AvenirNext-Regular.ttf in Resources */, 11F5D53B2EFDA17000A66466 /* modern.icon in Resources */, D71C4A4E1F520F1B00EBA30B /* MPreview.bundle in Resources */, D7CD5F681F508E74006AA35D /* SourceCodePro-Bold.ttf in Resources */, D7F2F19D21C503F000E41811 /* AvenirNext-Medium.ttf in Resources */, D7F2F1B821C503F000E41811 /* AvenirNext-Bold.ttf in Resources */, D7CD5F691F508E74006AA35D /* SourceCodePro-BoldIt.ttf in Resources */, D720241522A9412B000A7691 /* TextBundle.icns in Resources */, D7F2F1A621C503F000E41811 /* AvenirNext-BoldItalic.ttf in Resources */, D7DD795B1F4E611D00D5724B /* SourceCodePro-Regular.ttf in Resources */, D7F2F1A321C503F000E41811 /* AvenirNext-DemiItalic.ttf in Resources */, D7F2F1AC21C503F000E41811 /* AvenirNext-Light.ttf in Resources */, D7465F28207F2CD600E46A52 /* Images.xcassets in Resources */, D7F2F1BB21C503F000E41811 /* AvenirNext-Thin.ttf in Resources */, D7D79C28236798C300898A2D /* Welcome.bundle in Resources */, D7DD79581F4E60D000D5724B /* SourceCodePro-Black.ttf in Resources */, D720241222A9412B000A7691 /* Text.icns in Resources */, D7F2F1B221C503F000E41811 /* AvenirNext-HeavyItalic.ttf in Resources */, D7F2F1B521C503F000E41811 /* AvenirNext-ThinItalic.ttf in Resources */, D720241922A941A3000A7691 /* EncryptedTextPack.icns in Resources */, D7F2F1AF21C503F000E41811 /* AvenirNext-Demi.ttf in Resources */, D720240F22A9412B000A7691 /* Markdown.icns in Resources */, D7DD79591F4E60D000D5724B /* SourceCodePro-It.ttf in Resources */, 42E001C62ADAC2930099E7AD /* Localizable.xcstrings in Resources */, D7F2F1C121C503F000E41811 /* AvenirNext-LightItalic.ttf in Resources */, D7F2F1C421C503F000E41811 /* AvenirNext-UltraLightIt.ttf in Resources */, D7166F541F32F75E001A883F /* Main.storyboard in Resources */, D7F2F19721C503F000E41811 /* AvenirNext-MediumItalic.ttf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; D7E81C491F925B5F00416A91 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( D7F2F1BC21C503F000E41811 /* AvenirNext-Thin.ttf in Resources */, D73FABDA207F2EB600A98483 /* Images.xcassets in Resources */, D7E81C4B1F925B5F00416A91 /* MPreview.bundle in Resources */, D7E81C4C1F925B5F00416A91 /* SourceCodePro-Bold.ttf in Resources */, 11F5D53C2EFDA17000A66466 /* modern.icon in Resources */, D7F2F1B921C503F000E41811 /* AvenirNext-Bold.ttf in Resources */, D7F2F1BF21C503F000E41811 /* AvenirNext-Italic.ttf in Resources */, D7E81C4D1F925B5F00416A91 /* SourceCodePro-BoldIt.ttf in Resources */, D720241622A9412B000A7691 /* TextBundle.icns in Resources */, 42E001C72ADAC2930099E7AD /* Localizable.xcstrings in Resources */, D7F2F19E21C503F000E41811 /* AvenirNext-Medium.ttf in Resources */, D7F2F1B321C503F000E41811 /* AvenirNext-HeavyItalic.ttf in Resources */, D720241322A9412B000A7691 /* Text.icns in Resources */, D7E81C4E1F925B5F00416A91 /* SourceCodePro-Regular.ttf in Resources */, D7F2F1A121C503F000E41811 /* AvenirNext-Heavy.ttf in Resources */, D7F2F19821C503F000E41811 /* AvenirNext-MediumItalic.ttf in Resources */, D7F2F1A421C503F000E41811 /* AvenirNext-DemiItalic.ttf in Resources */, D7E81C4F1F925B5F00416A91 /* SourceCodePro-Black.ttf in Resources */, D720241022A9412B000A7691 /* Markdown.icns in Resources */, D7F2F19B21C503F000E41811 /* AvenirNext-UltraLight.ttf in Resources */, D7E81C501F925B5F00416A91 /* SourceCodePro-It.ttf in Resources */, D7F2F1B621C503F000E41811 /* AvenirNext-ThinItalic.ttf in Resources */, D7F2F1AD21C503F000E41811 /* AvenirNext-Light.ttf in Resources */, D7D79C29236798C300898A2D /* Welcome.bundle in Resources */, D7F2F1B021C503F000E41811 /* AvenirNext-Demi.ttf in Resources */, D7F2F1A721C503F000E41811 /* AvenirNext-BoldItalic.ttf in Resources */, D720241A22A941A3000A7691 /* EncryptedTextPack.icns in Resources */, D7E81C531F925B5F00416A91 /* Main.storyboard in Resources */, D7F2F1AA21C503F000E41811 /* AvenirNext-Regular.ttf in Resources */, D7F2F1C521C503F000E41811 /* AvenirNext-UltraLightIt.ttf in Resources */, D7F2F1C221C503F000E41811 /* AvenirNext-LightItalic.ttf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 064F52D71C0B9E9B54B4A092 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-FSNotes/Pods-FSNotes-frameworks.sh", "${BUILT_PRODUCTS_DIR}/MASShortcut/MASShortcut.framework", "${BUILT_PRODUCTS_DIR}/Punycode-macOS/Punycode.framework", "${BUILT_PRODUCTS_DIR}/RNCryptor-macOS/RNCryptor.framework", "${BUILT_PRODUCTS_DIR}/SSZipArchive-macOS/SSZipArchive.framework", "${BUILT_PRODUCTS_DIR}/libcmark_gfm-macOS/libcmark_gfm.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MASShortcut.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Punycode.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNCryptor.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libcmark_gfm.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-FSNotes/Pods-FSNotes-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 17481D1DB565210A80E68547 /* [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-FSNotes-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; }; 210B7CF3914C0A91C48CEFD3 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-FSNotes (iCloud)/Pods-FSNotes (iCloud)-frameworks.sh", "${BUILT_PRODUCTS_DIR}/MASShortcut/MASShortcut.framework", "${BUILT_PRODUCTS_DIR}/Punycode-macOS/Punycode.framework", "${BUILT_PRODUCTS_DIR}/RNCryptor-macOS/RNCryptor.framework", "${BUILT_PRODUCTS_DIR}/SSZipArchive-macOS/SSZipArchive.framework", "${BUILT_PRODUCTS_DIR}/libcmark_gfm-macOS/libcmark_gfm.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MASShortcut.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Punycode.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNCryptor.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libcmark_gfm.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-FSNotes (iCloud)/Pods-FSNotes (iCloud)-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; B3D3D9AF57B99BB9F7A7F798 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-FSNotes iOS/Pods-FSNotes iOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/Punycode-iOS/Punycode.framework", "${BUILT_PRODUCTS_DIR}/RNCryptor-iOS/RNCryptor.framework", "${BUILT_PRODUCTS_DIR}/SSZipArchive-iOS/SSZipArchive.framework", "${BUILT_PRODUCTS_DIR}/libcmark_gfm-iOS/libcmark_gfm.framework", "${BUILT_PRODUCTS_DIR}/CropViewController/CropViewController.framework", "${BUILT_PRODUCTS_DIR}/DropDown/DropDown.framework", "${BUILT_PRODUCTS_DIR}/SwipeCellKit/SwipeCellKit.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Punycode.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNCryptor.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libcmark_gfm.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CropViewController.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DropDown.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwipeCellKit.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-FSNotes iOS/Pods-FSNotes iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; B7738C934BDFDCE4A24683FE /* [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-FSNotes (iCloud)-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; }; D2B474F12776435E885D6144 /* [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-FSNotes iOS-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; }; F8CAD21668D14BDB8E66E536 /* [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-FSNotes iOS Share Extension-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 */ D75F3332205EC34800CC887E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 11F2D4F72F104322002E4E47 /* Project+Date.swift in Sources */, 11598DA42EDCB9B40036E387 /* UIFont+.swift in Sources */, D730BD38222DB11E00E69C93 /* TextBundleInfo.swift in Sources */, D792DD9427A6D6F5006ADC01 /* String+.swift in Sources */, D72DAF0C29B27D75001243BB /* ProjectSettings.swift in Sources */, D72682AE29BE8E2100F6E961 /* RepositoryAction.swift in Sources */, D79651B52517741400333AD4 /* ProgressState.swift in Sources */, D7680FB625D02B2C00810DA8 /* FileManager+.swift in Sources */, D7737394223D59D300154B9E /* KeychainPasswordItem.swift in Sources */, D7CD5CD121820CCD0009D63B /* Date+.swift in Sources */, D7CD5CC7218209BD0009D63B /* SortBy.swift in Sources */, D7B34F9B25195D7E0007877E /* PreviewState.swift in Sources */, D77E0539246312B400AD7772 /* StorageType.swift in Sources */, D7CD5CD521820D700009D63B /* ImagesProcessor.swift in Sources */, 113685522EC795B80033767F /* Data+.swift in Sources */, D79A13CE2A0E9C980037510B /* UIColor+.swift in Sources */, D7E9FEBC2C4AA64B0025D8E3 /* SearchQuery.swift in Sources */, 1136855F2EC7A6A50033767F /* Platform.swift in Sources */, D7B2B6EC245EEA790084B78D /* LanguageType.swift in Sources */, D7CD5CC9218209D80009D63B /* NoteType.swift in Sources */, D7CD5CC5218209820009D63B /* UserDefaultsManagement.swift in Sources */, 1136855A2EC7A21F0033767F /* NSMutableAttributedString+.swift in Sources */, D75F3339205EC34800CC887E /* ShareViewController.swift in Sources */, D7104A68230BD8C500B6D8EE /* SortDirection.swift in Sources */, 113685532EC796EF0033767F /* Note.swift in Sources */, D76F3682272563EC00D1FFB4 /* NSAttributedString+.swift in Sources */, D7CD5CD021820CBC0009D63B /* URL+.swift in Sources */, D7958A3A22ED512D00EDBDDC /* SandboxBookmark.swift in Sources */, D7CD5CCE21820C9D0009D63B /* DateFormatter+.swift in Sources */, 113A31A02EEE2D47009B50B0 /* Note+Preview.swift in Sources */, D7B4AC622471253100F3888A /* NoteMeta.swift in Sources */, D75627D226D1165A000AF6EA /* ImageFormat.swift in Sources */, D7E9FEBA2C4AA59D0025D8E3 /* SidebarItemType.swift in Sources */, 11BD8FAB2EDE1AF8000673A7 /* UserDataService.swift in Sources */, D7CD5CC42181F7530009D63B /* Storage.swift in Sources */, 110E409F2EA0150300C62F49 /* NSTextCheckingResult+.swift in Sources */, D7CD5CC6218209960009D63B /* Project.swift in Sources */, D7CD5CD421820D640009D63B /* NSAttributedStringKey+.swift in Sources */, D792DD8627A6C980006ADC01 /* FSParser.swift in Sources */, D730BD3F222DB9FC00E69C93 /* NameHelper.swift in Sources */, D7737393223D59CF00154B9E /* KeychainConfiguration.swift in Sources */, D7CD5CDC21832C190009D63B /* UserDefaultsManagement+.swift in Sources */, 113685642EC869950033767F /* URL+Image.swift in Sources */, D7BAC631249D1204008D29AA /* SettingsFilesNaming.swift in Sources */, 110D09842E9C152B001555FA /* NSRange+.swift in Sources */, D730BD31222DABA100E69C93 /* NoteContainer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; D7679382201F21F5000F7BBF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( D70E9E012901AEB600A3C634 /* DiffEntry.swift in Sources */, D70E9DF42901AEA800A3C634 /* Commit.swift in Sources */, D70E9E172901AEF300A3C634 /* Tags.swift in Sources */, D73FAE9F21553CAA0058BE61 /* UIApplication+.swift in Sources */, D79651B42517741400333AD4 /* ProgressState.swift in Sources */, D70E9E0A2901AECF00A3C634 /* Remote.swift in Sources */, 110BE0112EE86B4A00C5E456 /* Clojure.swift in Sources */, D70E9DF32901AEA100A3C634 /* PasswordAuthentication.swift in Sources */, D70E9E132901AEE600A3C634 /* Statuses.swift in Sources */, D792DD8527A6C980006ADC01 /* FSParser.swift in Sources */, 110BE0152EE8C1C900C5E456 /* Html.swift in Sources */, D794559A27C1B3F9000C283F /* ExternalViewController.swift in Sources */, 8F7136F023490CBF004DFA6E /* Markdown.swift in Sources */, D7DA9E2321033834001CF0BE /* NSMutableAttributedString+.swift in Sources */, 11BD8F9B2EDDF336000673A7 /* SolarizedDark.swift in Sources */, 11BD8F972EDDF32E000673A7 /* SolarizedLight.swift in Sources */, D70E9DF12901AE9B00A3C634 /* KeyAuthentication.swift in Sources */, D7A9C1DC29107B7600905619 /* Reference+Target.swift in Sources */, 11BD8FAA2EDE1AF8000673A7 /* UserDataService.swift in Sources */, D71FD21F2101B2D5008BEFA1 /* NoteAttachment.swift in Sources */, D73290BA2099F0AB0003F647 /* UndoData.swift in Sources */, D70E9E122901AEE100A3C634 /* FileHistoryIterator.swift in Sources */, D74B7B692137EFA9007F5331 /* SingleTouchDownGestureRecognizer.swift in Sources */, D736DDAD27BAC7940012ED70 /* Note+History.swift in Sources */, D730BD45223510A700E69C93 /* KeychainPasswordItem.swift in Sources */, D7CBAFFE214D5A1C002ECD5A /* ShortcutIdentifier.swift in Sources */, D785805C27A3483B000C1BAF /* FolderPopoverActions.swift in Sources */, D70E9E0C2901AEDA00A3C634 /* Repository+Lookup.swift in Sources */, D7A549C324DD9D3400537544 /* SettingsEditorViewController.swift in Sources */, D70E9DF02901AE9700A3C634 /* BranchesIterator.swift in Sources */, D70E9DEF2901AE9400A3C634 /* Branches.swift in Sources */, D7958A3922ED512D00EDBDDC /* SandboxBookmark.swift in Sources */, D74B7B672137D3A1007F5331 /* AttributedBox.swift in Sources */, 11D9431E2E643F250010CC2B /* Swift.swift in Sources */, D70E9DF52901AEAF00A3C634 /* Blob.swift in Sources */, 11F2D4F42F104322002E4E47 /* Project+Date.swift in Sources */, D7E6D9D320808623003ECAFC /* SidebarItem.swift in Sources */, D7487FEB2174E62A00D09383 /* NSAttributedStringKey+.swift in Sources */, D70E9DFA2901AEAF00A3C634 /* Signature.swift in Sources */, D7CC44C92A1E5F7600743857 /* ApiResponse.swift in Sources */, D70E9E1A2901AEF900A3C634 /* Tree.swift in Sources */, D70E9DFB2901AEAF00A3C634 /* Object.swift in Sources */, D7104A67230BD8C500B6D8EE /* SortDirection.swift in Sources */, D75627D126D1165A000AF6EA /* ImageFormat.swift in Sources */, D736DDAB27BABFFB0012ED70 /* RevisionsViewController.swift in Sources */, D730BD37222DB11E00E69C93 /* TextBundleInfo.swift in Sources */, D7503460285F827800086424 /* DateFormatter+.swift in Sources */, 113A31A12EEE2D47009B50B0 /* Note+Preview.swift in Sources */, D7D03BAF205C250500D96A6D /* FontViewController.swift in Sources */, 1136856A2EC8AE2F0033767F /* UIBarButtonItem+.swift in Sources */, 11BF066B2EE331B5006C7336 /* Scala.swift in Sources */, D75629B327D4DE9F00F55588 /* CodeThemeViewController.swift in Sources */, D7FDA4F7236DBC6900C3B4AA /* Sidebar.swift in Sources */, 113685682EC889E20033767F /* SceneDelegate.swift in Sources */, D71FD2252101CFD0008BEFA1 /* UITextView+.swift in Sources */, D70E9E052901AEC400A3C634 /* Index+Files.swift in Sources */, D70E9DF82901AEAF00A3C634 /* Strings.swift in Sources */, 11D6C0F12EE22CBC006017F0 /* Matlab.swift in Sources */, 11BD8FA72EDE0683000673A7 /* Theme.swift in Sources */, D7194B872023863A0062F1E3 /* ImagesProcessor.swift in Sources */, D70E9E022901AEBF00A3C634 /* Head.swift in Sources */, 11598DA32EDCB8D40036E387 /* UIFont+.swift in Sources */, 11D9431C2E643EF40010CC2B /* JavaScript.swift in Sources */, D7BDFE68201F67DA00897A58 /* NotesTextProcessor.swift in Sources */, 11D6C0F32EE22D3B006017F0 /* Dart.swift in Sources */, D730BD30222DABA100E69C93 /* NoteContainer.swift in Sources */, D7D9503F209D846E001FB60B /* SidebarTableCellView.swift in Sources */, D7C9029523547A1E00A89BD8 /* FSTag.swift in Sources */, D768D75A245E86690028F344 /* NSAttributedString+.swift in Sources */, D75A34E527D7CD440085438F /* SidebarViewController.swift in Sources */, 110BE0212EE8C53C00C5E456 /* TypeScript.swift in Sources */, D714496220C72D3500D7AD46 /* UIImage+.swift in Sources */, D70E9DFF2901AEAF00A3C634 /* Errors.swift in Sources */, D7470D092170E890006B2A92 /* NSTextStorage++.swift in Sources */, 113685632EC869950033767F /* URL+Image.swift in Sources */, D70E9DF72901AEAF00A3C634 /* ConfigManager.swift in Sources */, 11D6C0E92EE22C11006017F0 /* R.swift in Sources */, D70E9E102901AEDA00A3C634 /* RepositoryManager.swift in Sources */, 11D6C0CF2EE2279E006017F0 /* Cpp.swift in Sources */, D73794BF2336642500E75A28 /* (null) in Sources */, D70E9E062901AEC400A3C634 /* Index.swift in Sources */, D70E9DF92901AEAF00A3C634 /* Progress.swift in Sources */, D7C6DB5B25AA880600F8F76F /* ViewController+More.swift in Sources */, 11AF63402E898435004E7157 /* Sql.swift in Sources */, D70E9E082901AEC900A3C634 /* Reference.swift in Sources */, D70E9DEE2901AE9100A3C634 /* Branch.swift in Sources */, D70E9E042901AEBF00A3C634 /* Head+Merge.swift in Sources */, 1166D1F92E91BA8800B061CA /* CodeBlockDetector.swift in Sources */, D70E9E0D2901AEDA00A3C634 /* Repository+Commit.swift in Sources */, D70E9E192901AEF900A3C634 /* TreeEntry.swift in Sources */, D736DDA927B5DD370012ED70 /* EditorSelectionRect.swift in Sources */, 110BE01A2EE8C25100C5E456 /* Css.swift in Sources */, 11BF06772EE49546006C7336 /* Lua.swift in Sources */, 11BD8F942EDDEC3D000673A7 /* GitHubDark.swift in Sources */, 11D6C0E52EE22BF2006017F0 /* Kotlin.swift in Sources */, D750345F285F81F300086424 /* URL+.swift in Sources */, D7680FB525D02B2C00810DA8 /* FileManager+.swift in Sources */, D7BDFE6A201F68B700897A58 /* NotesTableView.swift in Sources */, D730BD3E222DB9FC00E69C93 /* NameHelper.swift in Sources */, D7CF7EAB29E2093C00FEC0C5 /* SecurityViewController.swift in Sources */, D7BDFE62201F678C00897A58 /* Note.swift in Sources */, 11D943272E643F630010CC2B /* GitHubLight.swift in Sources */, D77015822C972B7500CFF0E8 /* SettingsTableViewCell.swift in Sources */, D71354042042AFC800E3776F /* SettingsViewController.swift in Sources */, D7FB716C2BE7F66500808E56 /* EditorViewController+QuickLook.swift in Sources */, D70716DC2307E82900B44B0D /* SingleImageTouchDownGestureRecognizer.swift in Sources */, D70E9E072901AEC400A3C634 /* Index+Commit.swift in Sources */, D75629B527D5036D00F55588 /* SortByViewController.swift in Sources */, D70E9DFC2901AEAF00A3C634 /* Wrapper.swift in Sources */, D75629B127D4DB7E00F55588 /* CodeFontViewController.swift in Sources */, D71AA0222143A4A8004AFD2A /* MoveViewController.swift in Sources */, D7F6CFF02056AC22008C584A /* LanguageViewController.swift in Sources */, 11D6C0CB2EE225E7006017F0 /* C.swift in Sources */, D71760932826CAC4009794D8 /* MPreviewView.swift in Sources */, D7CDE9DE216178DE00DC5978 /* AppearanceType.swift in Sources */, 110BE0272EE8C5B000C5E456 /* Lisp.swift in Sources */, D77AD7FF27F9D1C90077BD45 /* Data+.swift in Sources */, 11BD8F9E2EDE0235000673A7 /* AtomOneLight.swift in Sources */, 11D6C0DC2EE22B0F006017F0 /* Rust.swift in Sources */, D7BDFE5D201F677B00897A58 /* UserDefaultsManagement.swift in Sources */, D7E6D9D42080862F003ECAFC /* SidebarItemType.swift in Sources */, D7C803EE2046DBBD005DA599 /* DefaultExtensionControllerView.swift in Sources */, D76025B4204EEF64000B9F59 /* TextFormatter.swift in Sources */, D7CC44C42A1E5E4F00743857 /* ViewController+WebApi.swift in Sources */, 11F389562EEA10930008EC18 /* Mermaid.swift in Sources */, D78115672153D4D9004FA1CA /* ProjectsViewController.swift in Sources */, D7BDFE60201F677B00897A58 /* NoteType.swift in Sources */, D792DD9927A6D71C006ADC01 /* String+Punycode.swift in Sources */, D709C9E229AFD9E0006EF9A8 /* GitTableViewCell.swift in Sources */, 11D6C0D32EE227F9006017F0 /* Java.swift in Sources */, D70E9E032901AEBF00A3C634 /* Head+Checkout.swift in Sources */, D752D80B23454750006842F9 /* NSTextAttachment+.swift in Sources */, D714749B279CE8EE001A8B29 /* MainNavigationController.swift in Sources */, 11D6C0EC2EE22C6B006017F0 /* Ruby.swift in Sources */, D750345E285F817D00086424 /* String+.swift in Sources */, 11BF06702EE33201006C7336 /* Bash.swift in Sources */, D714749D279D7DBC001A8B29 /* SearchQuery.swift in Sources */, D706396A202230BB00BC8446 /* EditorViewController.swift in Sources */, D70E9DF22901AE9E00A3C634 /* Authentication.swift in Sources */, D7BDFE61201F677B00897A58 /* SortBy.swift in Sources */, 11D6C0C92EE2256B006017F0 /* Python.swift in Sources */, D76E10C1215A55CE0017F4A3 /* Date+.swift in Sources */, 11D6C1042EE22E8F006017F0 /* Groovy.swift in Sources */, D7BDFE59201F671900897A58 /* Storage.swift in Sources */, 1136855E2EC7A6A50033767F /* Platform.swift in Sources */, 11BF067A2EE495C2006C7336 /* Perl.swift in Sources */, 110BE01F2EE8C3BE00C5E456 /* Shell.swift in Sources */, D70E9DF62901AEAF00A3C634 /* Error.swift in Sources */, D70E9E182901AEF300A3C634 /* Tag.swift in Sources */, 11D6C1092EE22EED006017F0 /* ObjectiveC.swift in Sources */, D7BB2E0129A0157700D5055A /* Storage+Git.swift in Sources */, D70E9E002901AEB600A3C634 /* Diff.swift in Sources */, D7B4AC612471253100F3888A /* NoteMeta.swift in Sources */, D70E9DFD2901AEAF00A3C634 /* OID.swift in Sources */, D78115632153B36C004FA1CA /* Buttons.swift in Sources */, D70E9E162901AEF300A3C634 /* TagIterator.swift in Sources */, 11BD8FA22EDE024D000673A7 /* AtomOneDark.swift in Sources */, D7A9C1D92910784400905619 /* Project+Git.swift in Sources */, D73673A820D10CF2000BA61D /* CloudDriveManager.swift in Sources */, D7EDEDFD21920402000B8C1A /* NoteCellView+.swift in Sources */, D767938B201F21F5000F7BBF /* ViewController.swift in Sources */, D781156B2153E05A004FA1CA /* ProjectSettingsViewController.swift in Sources */, D70E9E0E2901AEDA00A3C634 /* Repository+Open.swift in Sources */, D7BAC62F249D11F8008D29AA /* SettingsFilesNaming.swift in Sources */, D75629B727D53EB100F55588 /* ThanksViewController.swift in Sources */, 11B3F5962F182E5900A3531D /* EditorViewController+Search.swift in Sources */, D77E0538246312B400AD7772 /* StorageType.swift in Sources */, 11BF067E2EE4968C006C7336 /* Erlang.swift in Sources */, D7B34F9A25195D7E0007877E /* PreviewState.swift in Sources */, D72682AD29BE8E2000F6E961 /* RepositoryAction.swift in Sources */, 110D09852E9C152B001555FA /* NSRange+.swift in Sources */, 111013182ECA102200B6CF1B /* Pasteboard.swift in Sources */, 110E40A12EA0150300C62F49 /* NSTextCheckingResult+.swift in Sources */, 11D6C0FD2EE22E00006017F0 /* Assembly.swift in Sources */, D70E9E0F2901AEDA00A3C634 /* Repository.swift in Sources */, D70E9E152901AEEB00A3C634 /* Status.swift in Sources */, 11D6C1002EE22E4A006017F0 /* Scratch.swift in Sources */, 11D943222E643F410010CC2B /* Php.swift in Sources */, 1161828F2E637E31005B5EE0 /* SwiftHighlighter.swift in Sources */, D7C33F6E29E09A690006C473 /* AppIconViewController.swift in Sources */, 11BF06742EE33262006C7336 /* Haskell.swift in Sources */, D70E9DFE2901AEAF00A3C634 /* StaticSshKeyDelegate.swift in Sources */, D7E7DB3327A9B17000408725 /* DatePickerViewController.swift in Sources */, D71B9D892868BF7F00D2F323 /* TextStorageProcessor.swift in Sources */, D7CD5CDB21832C190009D63B /* UserDefaultsManagement+.swift in Sources */, D79A13CD2A0E9C980037510B /* UIColor+.swift in Sources */, 11D6C0F82EE22D78006017F0 /* Vb.swift in Sources */, D72DAF0B29B27D75001243BB /* ProjectSettings.swift in Sources */, D7679389201F21F5000F7BBF /* AppDelegate.swift in Sources */, D730BD46223510A900E69C93 /* KeychainConfiguration.swift in Sources */, 11D6C0E02EE22B77006017F0 /* Csharp.swift in Sources */, D7BDFE70201F788D00897A58 /* NoteCellView.swift in Sources */, D73794C123366F5200E75A28 /* ImageScrollView.swift in Sources */, D73B3135298FBF4400F46144 /* GitViewController.swift in Sources */, D730BD5C223BFEB200E69C93 /* RuntimeError.swift in Sources */, D7B2B6EA245EEA620084B78D /* LanguageType.swift in Sources */, D70E9E142901AEEB00A3C634 /* StatusIterator.swift in Sources */, 111013152EC8F1B600B6CF1B /* ImagePreviewViewController.swift in Sources */, 11D702AC2E5B8E0C004DBAEC /* HtmlExtractor.swift in Sources */, 11D6C0D92EE229E9006017F0 /* Go.swift in Sources */, D794558E27C05743000C283F /* ProViewController.swift in Sources */, D7BDFE6C201F6DC200897A58 /* EditTextView.swift in Sources */, D70E9E0B2901AED400A3C634 /* Remotes.swift in Sources */, D7D9503D209D806F001FB60B /* SidebarTableView.swift in Sources */, D70E9E112901AEE100A3C634 /* RevisionIterator.swift in Sources */, D7B6E59D20794B8C00FE0E20 /* Project.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; D7793C6B1F211C6000CA39B7 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( D73BCCAA28EB5EC3008B3BBC /* OID.swift in Sources */, D7D97F3C290437A200C651D4 /* NSWindowController+.swift in Sources */, D7DA9E1E21031901001CF0BE /* OutlineHeaderView.swift in Sources */, D7D01AFD2C65203A00F545D0 /* PrinterLegacy.swift in Sources */, 11D943292E643F630010CC2B /* GitHubLight.swift in Sources */, D7B3FE7121027A6D00764C39 /* UserDataService.swift in Sources */, 11F3F4872EDB0E4400435CBF /* DateFormatter+.swift in Sources */, D7315ED2215ED15500AB49D4 /* SidebarSplitView.swift in Sources */, 11D6C0E72EE22C11006017F0 /* R.swift in Sources */, 11AF633E2E898435004E7157 /* Sql.swift in Sources */, 117C0E4D2EEDB9C30086419C /* EditTextView+Clicked.swift in Sources */, D73BCCF228EB5EC4008B3BBC /* TreeEntry.swift in Sources */, 11BF067B2EE495C2006C7336 /* Perl.swift in Sources */, D73BCD0428EB5EC4008B3BBC /* Repository+Commit.swift in Sources */, D7E6ACE920832D41003599A2 /* AppDelegate+URLRoutes.swift in Sources */, 11D6C0CD2EE225E7006017F0 /* C.swift in Sources */, 110BE0192EE8C25100C5E456 /* Css.swift in Sources */, D73BCCDA28EB5EC4008B3BBC /* Status.swift in Sources */, D771E96F28EDFBF600CD4871 /* Errors.swift in Sources */, D752D80823454750006842F9 /* NSTextAttachment+.swift in Sources */, 11D6C1072EE22EED006017F0 /* ObjectiveC.swift in Sources */, D743FB5424CD72B1003A8913 /* SettingsFilesNaming.swift in Sources */, D7170C1D20F8565B001DDB36 /* FileSystemEventManager.swift in Sources */, D71B9D7A2867027000D2F323 /* NoteViewController.swift in Sources */, D73BCC9828EB5EC3008B3BBC /* Strings.swift in Sources */, D73BCCB028EB5EC3008B3BBC /* Object.swift in Sources */, 1102DDB12EE4C280005029A6 /* EditTextView+Complete.swift in Sources */, D7B34F9725195D7E0007877E /* PreviewState.swift in Sources */, D779C7BB1F415C0300FADEE1 /* PrefsWindowController.swift in Sources */, D73BCCAD28EB5EC3008B3BBC /* Signature.swift in Sources */, 110D09832E9C152B001555FA /* NSRange+.swift in Sources */, D7793C751F211C6000CA39B7 /* ViewController.swift in Sources */, D7B13DBD2C64F445008EBCAA /* Printer.swift in Sources */, D7DD5A881F88D4EA00CE947E /* FileWatcher.swift in Sources */, D77A12382C469AD0001B388B /* SearchQuery.swift in Sources */, 11BF06722EE33262006C7336 /* Haskell.swift in Sources */, 11D6C0E42EE22BF2006017F0 /* Kotlin.swift in Sources */, D7D3D2A427BEEA61001C1497 /* Note+History.swift in Sources */, D7F95F3A1FF2759300E2A447 /* NotesTextProcessor.swift in Sources */, D73BCCF528EB5EC4008B3BBC /* Tree.swift in Sources */, D792DD8227A6C980006ADC01 /* FSParser.swift in Sources */, D730BD5A223BFEB200E69C93 /* RuntimeError.swift in Sources */, D73BCCCB28EB5EC4008B3BBC /* Authentication.swift in Sources */, D73BCC9528EB5EC3008B3BBC /* BranchesIterator.swift in Sources */, D73BCD0728EB5EC4008B3BBC /* Reference.swift in Sources */, D7B6E59A207912E300FE0E20 /* Project.swift in Sources */, D777D7812009115C00D86B33 /* ImagesProcessor.swift in Sources */, D708AC672000EF5800A1760F /* NoteType.swift in Sources */, D73BCC9B28EB5EC3008B3BBC /* ConfigManager.swift in Sources */, D73BCCBF28EB5EC3008B3BBC /* FileHistoryIterator.swift in Sources */, D7508FCE1F3438540047AB76 /* PrefsViewController.swift in Sources */, D792297521A845B4005F468F /* ProjectSettingsViewController.swift in Sources */, D7315ED5215ED95600AB49D4 /* NSAppearance+.swift in Sources */, D7DA9E2121033489001CF0BE /* NSMutableAttributedString+.swift in Sources */, D73BCCE628EB5EC4008B3BBC /* Index+Files.swift in Sources */, D73BCCD428EB5EC4008B3BBC /* Remotes.swift in Sources */, D73BCCD128EB5EC4008B3BBC /* Remote.swift in Sources */, D76447DC1F3A4F0700965F01 /* UserDefaultsManagement.swift in Sources */, 2799407C218484C900727B20 /* TitleBarView.swift in Sources */, 11F3F4832EDB0E0B00435CBF /* URL+.swift in Sources */, D73BCC8C28EB5EC3008B3BBC /* Commit.swift in Sources */, D796EB41251E127300CE5C80 /* Pasteboard.swift in Sources */, D730BD2A222BF32A00E69C93 /* KeychainPasswordItem.swift in Sources */, D75EE7FD2078C5460055F159 /* SidebarCellView.swift in Sources */, D797004C1F3DD10700BAD94D /* EditTextView.swift in Sources */, D73BCCC228EB5EC3008B3BBC /* DiffEntry.swift in Sources */, D7BB2DFE29A0157700D5055A /* Storage+Git.swift in Sources */, D77E0537246312B300AD7772 /* StorageType.swift in Sources */, 113A31A22EEE2D47009B50B0 /* Note+Preview.swift in Sources */, D7EDEDFB219203C9000B8C1A /* NoteCellView+.swift in Sources */, D7A65C5920F11C38003E5ADC /* LanguageType.swift in Sources */, D73BCCEF28EB5EC4008B3BBC /* Head+Merge.swift in Sources */, D7CB9905207E5AE300037E91 /* SidebarTableRowView.swift in Sources */, D7B4AC5E2471253100F3888A /* NoteMeta.swift in Sources */, 11BD8F932EDDEC3D000673A7 /* GitHubDark.swift in Sources */, 11D6C0C72EE2256B006017F0 /* Python.swift in Sources */, D730BD27222BF30700E69C93 /* KeychainConfiguration.swift in Sources */, D7487FD821738A1900D09383 /* NSImage+.swift in Sources */, 11D9431A2E643EF40010CC2B /* JavaScript.swift in Sources */, 110BE0262EE8C5B000C5E456 /* Lisp.swift in Sources */, D75EE7F72078B22D0055F159 /* SidebarItem.swift in Sources */, D783B505208A1BFD00328A41 /* EditorSplitView.swift in Sources */, D726DE8A287ACC1E00F8406C /* NSWindow+.swift in Sources */, D75EE8002078E0C60055F159 /* SidebarItemType.swift in Sources */, D7793C731F211C6000CA39B7 /* AppDelegate.swift in Sources */, D7F5C0F8223ED57D0038F172 /* PreferencesSecurityViewController.swift in Sources */, 11F018B22EF8415600F07580 /* MPreviewContainerView.swift in Sources */, D73BCCC828EB5EC4008B3BBC /* KeyAuthentication.swift in Sources */, 11D6C0FB2EE22E00006017F0 /* Assembly.swift in Sources */, D7680FB225D02B2C00810DA8 /* FileManager+.swift in Sources */, D73BCCE328EB5EC4008B3BBC /* Index+Commit.swift in Sources */, D79C26252872384C00CB70E6 /* EditorViewController+Sharing.swift in Sources */, D77F89DF28D38B5D00BECC87 /* ViewController+Web.swift in Sources */, D73BCD0128EB5EC4008B3BBC /* Repository+Lookup.swift in Sources */, D7E51713220D814D00A9CAD9 /* UTI.swift in Sources */, 11BD8FA32EDE024D000673A7 /* AtomOneDark.swift in Sources */, D71B9D862868BF7F00D2F323 /* TextStorageProcessor.swift in Sources */, D730829223084340003185D1 /* MPreviewView.swift in Sources */, D75EE7FA2078B3C00055F159 /* Sidebar.swift in Sources */, 11D6C0ED2EE22C6B006017F0 /* Ruby.swift in Sources */, 11A6A8FD2EF074D8005D000A /* EditTextView+Todo.swift in Sources */, D70B1FAB29213EDF003923DC /* HyperlinkTextField.swift in Sources */, 1166D1F72E91BA8800B061CA /* CodeBlockDetector.swift in Sources */, 11D6C0D72EE229E9006017F0 /* Go.swift in Sources */, 11BD8FA02EDE0235000673A7 /* AtomOneLight.swift in Sources */, 11D702AD2E5B8E0C004DBAEC /* HtmlExtractor.swift in Sources */, D7D1DE68216D05A800AC1845 /* NameTextField.swift in Sources */, D735E5BF1F2F001500173215 /* NoteRowView.swift in Sources */, D7CA7FD4232652E300E9717A /* PreferencesGitViewController.swift in Sources */, D7CD5CCD21820B7B0009D63B /* UserDefaultsManagement+.swift in Sources */, D76025B2204EEF64000B9F59 /* TextFormatter.swift in Sources */, 11D943242E643F410010CC2B /* Php.swift in Sources */, D7038E2620FB24E000A54E69 /* NoteAttachment.swift in Sources */, D73BCCE028EB5EC4008B3BBC /* Index.swift in Sources */, D74112281FABA21B00AB619A /* MainWindow.swift in Sources */, D7CDE9DC2161767A00DC5978 /* AppearanceType.swift in Sources */, 11ABE5E22EEEFD0E00E7C9EB /* NotesCounterView.swift in Sources */, D7E025081F3B6DDB00EDDA32 /* Storage.swift in Sources */, D74DFBAD21661BA400F67D64 /* Date+.swift in Sources */, 113685602EC7A6A50033767F /* Platform.swift in Sources */, D7F5C0EF223ED0570038F172 /* PreferencesGeneralViewController.swift in Sources */, 11BD8F982EDDF32E000673A7 /* SolarizedLight.swift in Sources */, D7D372F7207BB09500AFBD9F /* SidebarOutlineView.swift in Sources */, D7CCEDB92C6BA2F300A3BB83 /* ClickableTextField.swift in Sources */, D72682AA29BE8E2000F6E961 /* RepositoryAction.swift in Sources */, 11D702A72E5ADDED004DBAEC /* LayoutManager.swift in Sources */, D73BCCFB28EB5EC4008B3BBC /* Repository.swift in Sources */, D73BCCC528EB5EC4008B3BBC /* Diff.swift in Sources */, D73BCCB328EB5EC3008B3BBC /* TagIterator.swift in Sources */, D73BCCDD28EB5EC4008B3BBC /* StatusIterator.swift in Sources */, D73BCCA128EB5EC3008B3BBC /* Wrapper.swift in Sources */, D7470D072170E890006B2A92 /* NSTextStorage++.swift in Sources */, 110E40A52EA039CE00C62F49 /* EditTextView+DragOperation.swift in Sources */, D772C8842217362C007E440B /* ViewController+Print.swift in Sources */, D7DD5A8A1F88D50000CE947E /* FileWatcherEvent.swift in Sources */, D73BCD0A28EB5EC4008B3BBC /* Reference+Target.swift in Sources */, D7CC44C62A1E5F7600743857 /* ApiResponse.swift in Sources */, 11AA4B1F2EF9A47A0075A9E4 /* EditorViewController+ScrollPosition.swift in Sources */, D73BCC8F28EB5EC3008B3BBC /* Branch.swift in Sources */, 110BE01E2EE8C3BE00C5E456 /* Shell.swift in Sources */, D73BCCBC28EB5EC3008B3BBC /* RevisionIterator.swift in Sources */, 11D6C0D12EE2279E006017F0 /* Cpp.swift in Sources */, 11D6C0D42EE227F9006017F0 /* Java.swift in Sources */, D7487FED2174E62B00D09383 /* NSAttributedStringKey+.swift in Sources */, D77AD7FC27F9D1C90077BD45 /* Data+.swift in Sources */, 11BF06782EE49546006C7336 /* Lua.swift in Sources */, D7CE196C1FA4BA5E004BF8EE /* PreviewTextField.swift in Sources */, D73BCCE928EB5EC4008B3BBC /* Head+Checkout.swift in Sources */, 11D6C1032EE22E8F006017F0 /* Groovy.swift in Sources */, D73BCCCE28EB5EC4008B3BBC /* PasswordAuthentication.swift in Sources */, D7679376201F0BFD000F7BBF /* SortBy.swift in Sources */, D7A415141F2FBDA00099B82C /* NotesTableView.swift in Sources */, D7D7CD3A232774BC0016AC15 /* ViewController+Git.swift in Sources */, D7BCF035296B0DAA00F72A4F /* AboutImageView.swift in Sources */, D7013E0026C3B116006F58E3 /* NSColor+.swift in Sources */, D7315ECF215ECF3000AB49D4 /* EditorView.swift in Sources */, D7A9C1D62910784400905619 /* Project+Git.swift in Sources */, D7153DFD2285A93300A2C20F /* AboutWindowController.swift in Sources */, 11F2D4F62F104322002E4E47 /* Project+Date.swift in Sources */, D74D479F256DF2EB00D97647 /* FSNTextAttachmentCell.swift in Sources */, 11BD8F9C2EDDF336000673A7 /* SolarizedDark.swift in Sources */, D77CC041216A608500582B97 /* EditorScrollView.swift in Sources */, 110BE0232EE8C53C00C5E456 /* TypeScript.swift in Sources */, 110BE0122EE86B4A00C5E456 /* Clojure.swift in Sources */, 11A6A8FA2EF06B9D005D000A /* EditTextView+MoveLines.swift in Sources */, 8F7136EE23490CBF004DFA6E /* Markdown.swift in Sources */, D773DE801F36F45900A39C9F /* SandboxBookmark.swift in Sources */, D7487F932173503C00D09383 /* AttributedBox.swift in Sources */, D730BD3C222DB9FC00E69C93 /* NameHelper.swift in Sources */, D7153E052285C09C00A2C20F /* AboutViewController.swift in Sources */, D73BCCB928EB5EC3008B3BBC /* Tag.swift in Sources */, 11BF066A2EE331B5006C7336 /* Scala.swift in Sources */, 11D6C0F52EE22D3B006017F0 /* Dart.swift in Sources */, D768D75C245ED6470028F344 /* VerticallyAlignedTextFieldCell.swift in Sources */, D7F5C0FB223ED58F0038F172 /* PreferencesAdvancedViewController.swift in Sources */, D7CC44C12A1E5E4F00743857 /* ViewController+WebApi.swift in Sources */, D7CD5F6C1F51185F006AA35D /* NSFont+.swift in Sources */, 110BE0172EE8C1C900C5E456 /* Html.swift in Sources */, 11D6C0E12EE22B77006017F0 /* Csharp.swift in Sources */, 11F389582EEA10930008EC18 /* Mermaid.swift in Sources */, D7508FC81F337E850047AB76 /* SearchTextField.swift in Sources */, 11F018AC2EF7E78600F07580 /* MPreviewFindPanel.swift in Sources */, D7F5C0F2223ED0C00038F172 /* PreferencesUserInterfaceViewController.swift in Sources */, D768D759245E86680028F344 /* NSAttributedString+.swift in Sources */, D73BCC9E28EB5EC3008B3BBC /* Error.swift in Sources */, 275592971F3AE9B5006B8988 /* MainWindowController.swift in Sources */, D73BCCFE28EB5EC4008B3BBC /* Repository+Open.swift in Sources */, D73BCCF828EB5EC4008B3BBC /* RepositoryManager.swift in Sources */, D71B9D822868658100D2F323 /* EditorViewController.swift in Sources */, D77A6F7F28B11496006A0353 /* PreferencesWebViewController.swift in Sources */, 11D6C0EF2EE22CBC006017F0 /* Matlab.swift in Sources */, D73BCCEC28EB5EC4008B3BBC /* Head.swift in Sources */, D73BCCD728EB5EC4008B3BBC /* Statuses.swift in Sources */, D72DAF0829B27D75001243BB /* ProjectSettings.swift in Sources */, D7F5C0F5223ED5620038F172 /* PreferencesEditorViewController.swift in Sources */, D738356D2242871400B260DD /* MasterPasswordViewController.swift in Sources */, 113685652EC869950033767F /* URL+Image.swift in Sources */, 11D9431F2E643F250010CC2B /* Swift.swift in Sources */, 11BF067F2EE4968C006C7336 /* Erlang.swift in Sources */, D73BCCA428EB5EC3008B3BBC /* Progress.swift in Sources */, D73BCCB628EB5EC3008B3BBC /* Tags.swift in Sources */, 11BF066E2EE33201006C7336 /* Bash.swift in Sources */, D7C1C99A235606CB0021A32D /* SidebarHeaderCellView.swift in Sources */, D7104A64230BD8C500B6D8EE /* SortDirection.swift in Sources */, D75627CE26D1165A000AF6EA /* ImageFormat.swift in Sources */, 11BD8FA82EDE0683000673A7 /* Theme.swift in Sources */, 11D6C0DD2EE22B0F006017F0 /* Rust.swift in Sources */, D735E5BD1F2EF66000173215 /* NoteCellView.swift in Sources */, D78678CB2093AE10001A6620 /* UndoData.swift in Sources */, D7E32C2C28F8D0740048614B /* StaticSshKeyDelegate.swift in Sources */, 11F3F4852EDB0E2A00435CBF /* String+.swift in Sources */, D7C9029223547A1E00A89BD8 /* FSTag.swift in Sources */, D730BD2E222DABA100E69C93 /* NoteContainer.swift in Sources */, D730BD35222DB11E00E69C93 /* TextBundleInfo.swift in Sources */, 11F1771A2EF1E93500CC566F /* ViewController+Menu.swift in Sources */, D79FE8A21F77D04A00113CFD /* Note.swift in Sources */, D73BCCA728EB5EC3008B3BBC /* Blob.swift in Sources */, D7153E092285EC6100A2C20F /* TitleTextField.swift in Sources */, D7D372F4207B5B0F00AFBD9F /* SidebarNotesView.swift in Sources */, 1161828D2E637E31005B5EE0 /* SwiftHighlighter.swift in Sources */, 110E40A02EA0150300C62F49 /* NSTextCheckingResult+.swift in Sources */, 11D6C0FF2EE22E4A006017F0 /* Scratch.swift in Sources */, D73BCC9228EB5EC3008B3BBC /* Branches.swift in Sources */, 11D6C0F92EE22D78006017F0 /* Vb.swift in Sources */, D79798A229C0FE6A00B9A878 /* SettingsViewController.swift in Sources */, D79651B12517741400333AD4 /* ProgressState.swift in Sources */, D792DD9627A6D71C006ADC01 /* String+Punycode.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; D7E81C2D1F925B5F00416A91 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( D73BCCAB28EB5EC3008B3BBC /* OID.swift in Sources */, D7D97F3D290437A200C651D4 /* NSWindowController+.swift in Sources */, D7DA9E1F21031901001CF0BE /* OutlineHeaderView.swift in Sources */, D7D01AFE2C65208000F545D0 /* PrinterLegacy.swift in Sources */, 11D943282E643F630010CC2B /* GitHubLight.swift in Sources */, D7B3FE7021027A5E00764C39 /* UserDataService.swift in Sources */, 11F3F4882EDB0E4400435CBF /* DateFormatter+.swift in Sources */, D7315ED3215ED15500AB49D4 /* SidebarSplitView.swift in Sources */, 11D6C0E82EE22C11006017F0 /* R.swift in Sources */, 11AF633F2E898435004E7157 /* Sql.swift in Sources */, 117C0E4E2EEDB9C40086419C /* EditTextView+Clicked.swift in Sources */, D73BCCF328EB5EC4008B3BBC /* TreeEntry.swift in Sources */, 11BF067C2EE495C2006C7336 /* Perl.swift in Sources */, D73BCD0528EB5EC4008B3BBC /* Repository+Commit.swift in Sources */, D7E6ACEA20832D41003599A2 /* AppDelegate+URLRoutes.swift in Sources */, 11D6C0CC2EE225E7006017F0 /* C.swift in Sources */, 110BE01B2EE8C25100C5E456 /* Css.swift in Sources */, D73BCCDB28EB5EC4008B3BBC /* Status.swift in Sources */, D771E97028EDFBF600CD4871 /* Errors.swift in Sources */, D752D80923454750006842F9 /* NSTextAttachment+.swift in Sources */, 11D6C1082EE22EED006017F0 /* ObjectiveC.swift in Sources */, D743FB5324CD72B0003A8913 /* SettingsFilesNaming.swift in Sources */, D7170C1E20F8565B001DDB36 /* FileSystemEventManager.swift in Sources */, D71B9D7B2867027000D2F323 /* NoteViewController.swift in Sources */, D73BCC9928EB5EC3008B3BBC /* Strings.swift in Sources */, D73BCCB128EB5EC3008B3BBC /* Object.swift in Sources */, 1102DDB22EE4C280005029A6 /* EditTextView+Complete.swift in Sources */, D7B34F9825195D7E0007877E /* PreviewState.swift in Sources */, D777D782200912A400D86B33 /* ImagesProcessor.swift in Sources */, D73BCCAE28EB5EC3008B3BBC /* Signature.swift in Sources */, 110D09862E9C152B001555FA /* NSRange+.swift in Sources */, D708AC682000F0E100A1760F /* NoteType.swift in Sources */, D7B13DBE2C64F445008EBCAA /* Printer.swift in Sources */, D7B6E59B207912E300FE0E20 /* Project.swift in Sources */, D77A12372C469ACF001B388B /* SearchQuery.swift in Sources */, 11BF06732EE33262006C7336 /* Haskell.swift in Sources */, 11D6C0E32EE22BF2006017F0 /* Kotlin.swift in Sources */, D7D3D2A527BEEA62001C1497 /* Note+History.swift in Sources */, D73BCCF628EB5EC4008B3BBC /* Tree.swift in Sources */, D730BD5B223BFEB200E69C93 /* RuntimeError.swift in Sources */, D792DD8327A6C980006ADC01 /* FSParser.swift in Sources */, D73BCCCC28EB5EC4008B3BBC /* Authentication.swift in Sources */, D73BCC9628EB5EC3008B3BBC /* BranchesIterator.swift in Sources */, D73BCD0828EB5EC4008B3BBC /* Reference.swift in Sources */, D7FFD09C1FF677ED0064CBA6 /* NotesTextProcessor.swift in Sources */, D75EE7FE2078C5460055F159 /* SidebarCellView.swift in Sources */, D73BCC9C28EB5EC3008B3BBC /* ConfigManager.swift in Sources */, D74112291FABA29100AB619A /* MainWindow.swift in Sources */, D73BCCC028EB5EC3008B3BBC /* FileHistoryIterator.swift in Sources */, D79F92121FA8B9E2008C297E /* PreviewTextField.swift in Sources */, D792297621A845B4005F468F /* ProjectSettingsViewController.swift in Sources */, D7315ED6215ED95600AB49D4 /* NSAppearance+.swift in Sources */, D7DA9E2221033489001CF0BE /* NSMutableAttributedString+.swift in Sources */, D73BCCE728EB5EC4008B3BBC /* Index+Files.swift in Sources */, D73BCCD528EB5EC4008B3BBC /* Remotes.swift in Sources */, D73BCCD228EB5EC4008B3BBC /* Remote.swift in Sources */, D7CB9906207E5AE300037E91 /* SidebarTableRowView.swift in Sources */, D73BCC8D28EB5EC3008B3BBC /* Commit.swift in Sources */, D7BA204D2186E3DD0064824B /* TitleBarView.swift in Sources */, 11F3F4842EDB0E0B00435CBF /* URL+.swift in Sources */, D730BD2B222BF32A00E69C93 /* KeychainPasswordItem.swift in Sources */, D796EB42251E127300CE5C80 /* Pasteboard.swift in Sources */, D75EE7F82078B22D0055F159 /* SidebarItem.swift in Sources */, D73BCCC328EB5EC3008B3BBC /* DiffEntry.swift in Sources */, D7A65C5A20F11C38003E5ADC /* LanguageType.swift in Sources */, D77E0536246312B200AD7772 /* StorageType.swift in Sources */, D7BB2DFF29A0157700D5055A /* Storage+Git.swift in Sources */, D7EDEDFC219203C9000B8C1A /* NoteCellView+.swift in Sources */, 113A31A32EEE2D47009B50B0 /* Note+Preview.swift in Sources */, D75EE8012078E0C60055F159 /* SidebarItemType.swift in Sources */, D73BCCF028EB5EC4008B3BBC /* Head+Merge.swift in Sources */, D7E81C2E1F925B5F00416A91 /* PrefsWindowController.swift in Sources */, D783B506208A1BFD00328A41 /* EditorSplitView.swift in Sources */, D7B4AC5F2471253100F3888A /* NoteMeta.swift in Sources */, 11BD8F922EDDEC3D000673A7 /* GitHubDark.swift in Sources */, 11D6C0C82EE2256B006017F0 /* Python.swift in Sources */, D730BD28222BF30700E69C93 /* KeychainConfiguration.swift in Sources */, D7487FD721738A1800D09383 /* NSImage+.swift in Sources */, 11D9431B2E643EF40010CC2B /* JavaScript.swift in Sources */, 110BE0252EE8C5B000C5E456 /* Lisp.swift in Sources */, D7E81C2F1F925B5F00416A91 /* ViewController.swift in Sources */, D75EE7FB2078B3C00055F159 /* Sidebar.swift in Sources */, D726DE8B287ACC1E00F8406C /* NSWindow+.swift in Sources */, D7E81C301F925B5F00416A91 /* FileWatcher.swift in Sources */, D73BCCC928EB5EC4008B3BBC /* KeyAuthentication.swift in Sources */, D7F5C0F9223ED57D0038F172 /* PreferencesSecurityViewController.swift in Sources */, 11F018B32EF8415600F07580 /* MPreviewContainerView.swift in Sources */, D73BCCE428EB5EC4008B3BBC /* Index+Commit.swift in Sources */, 11D6C0FC2EE22E00006017F0 /* Assembly.swift in Sources */, D7680FB325D02B2C00810DA8 /* FileManager+.swift in Sources */, D79C26262872384C00CB70E6 /* EditorViewController+Sharing.swift in Sources */, D77F89E028D38B5E00BECC87 /* ViewController+Web.swift in Sources */, D73BCD0228EB5EC4008B3BBC /* Repository+Lookup.swift in Sources */, D7E51714220D814D00A9CAD9 /* UTI.swift in Sources */, D71B9D872868BF7F00D2F323 /* TextStorageProcessor.swift in Sources */, 11BD8FA42EDE024D000673A7 /* AtomOneDark.swift in Sources */, D730829323084340003185D1 /* MPreviewView.swift in Sources */, D7E81C321F925B5F00416A91 /* PrefsViewController.swift in Sources */, D70B1FAC29213EE0003923DC /* HyperlinkTextField.swift in Sources */, 11D6C0EB2EE22C6B006017F0 /* Ruby.swift in Sources */, 11A6A8FE2EF074D8005D000A /* EditTextView+Todo.swift in Sources */, D76025B3204EEF64000B9F59 /* TextFormatter.swift in Sources */, 1166D1F82E91BA8800B061CA /* CodeBlockDetector.swift in Sources */, 11D6C0D82EE229E9006017F0 /* Go.swift in Sources */, 11BD8F9F2EDE0235000673A7 /* AtomOneLight.swift in Sources */, 11D702AE2E5B8E0C004DBAEC /* HtmlExtractor.swift in Sources */, D7D1DE69216D05A800AC1845 /* NameTextField.swift in Sources */, D7038E2720FB24E000A54E69 /* NoteAttachment.swift in Sources */, D7CA7FD5232652E300E9717A /* PreferencesGitViewController.swift in Sources */, D7CD5CCC21820B7A0009D63B /* UserDefaultsManagement+.swift in Sources */, D7E81C331F925B5F00416A91 /* UserDefaultsManagement.swift in Sources */, 11D943232E643F410010CC2B /* Php.swift in Sources */, D7E81C341F925B5F00416A91 /* EditTextView.swift in Sources */, D7E81C361F925B5F00416A91 /* AppDelegate.swift in Sources */, D73BCCE128EB5EC4008B3BBC /* Index.swift in Sources */, D7CDE9DD2161767B00DC5978 /* AppearanceType.swift in Sources */, 11ABE5E32EEEFD0E00E7C9EB /* NotesCounterView.swift in Sources */, D7D372F8207BB09500AFBD9F /* SidebarOutlineView.swift in Sources */, D74DFBAC21661BA300F67D64 /* Date+.swift in Sources */, 1136855C2EC7A6A50033767F /* Platform.swift in Sources */, D7F5C0F0223ED0570038F172 /* PreferencesGeneralViewController.swift in Sources */, 11BD8F962EDDF32E000673A7 /* SolarizedLight.swift in Sources */, D7470D082170E890006B2A92 /* NSTextStorage++.swift in Sources */, D7CCEDBA2C6BA2F300A3BB83 /* ClickableTextField.swift in Sources */, D72682AB29BE8E2000F6E961 /* RepositoryAction.swift in Sources */, 11D702A62E5ADDED004DBAEC /* LayoutManager.swift in Sources */, D73BCCFC28EB5EC4008B3BBC /* Repository.swift in Sources */, D73BCCC628EB5EC4008B3BBC /* Diff.swift in Sources */, D73BCCB428EB5EC3008B3BBC /* TagIterator.swift in Sources */, D73BCCDE28EB5EC4008B3BBC /* StatusIterator.swift in Sources */, D73BCCA228EB5EC3008B3BBC /* Wrapper.swift in Sources */, D7E81C371F925B5F00416A91 /* NoteRowView.swift in Sources */, 110E40A42EA039CE00C62F49 /* EditTextView+DragOperation.swift in Sources */, D772C8852217362C007E440B /* ViewController+Print.swift in Sources */, D7E81C381F925B5F00416A91 /* Storage.swift in Sources */, D73BCD0B28EB5EC4008B3BBC /* Reference+Target.swift in Sources */, D7CC44C72A1E5F7600743857 /* ApiResponse.swift in Sources */, 11AA4B202EF9A47A0075A9E4 /* EditorViewController+ScrollPosition.swift in Sources */, D73BCC9028EB5EC3008B3BBC /* Branch.swift in Sources */, 110BE01D2EE8C3BE00C5E456 /* Shell.swift in Sources */, D73BCCBD28EB5EC3008B3BBC /* RevisionIterator.swift in Sources */, 11D6C0D02EE2279E006017F0 /* Cpp.swift in Sources */, 11D6C0D52EE227F9006017F0 /* Java.swift in Sources */, D7487FEC2174E62B00D09383 /* NSAttributedStringKey+.swift in Sources */, D77AD7FD27F9D1C90077BD45 /* Data+.swift in Sources */, 11BF06762EE49546006C7336 /* Lua.swift in Sources */, D7679377201F0BFD000F7BBF /* SortBy.swift in Sources */, D7E81C391F925B5F00416A91 /* FileWatcherEvent.swift in Sources */, 11D6C1052EE22E8F006017F0 /* Groovy.swift in Sources */, D73BCCEA28EB5EC4008B3BBC /* Head+Checkout.swift in Sources */, D73BCCCF28EB5EC4008B3BBC /* PasswordAuthentication.swift in Sources */, D7D7CD3B232774BC0016AC15 /* ViewController+Git.swift in Sources */, D7BCF036296B0DAA00F72A4F /* AboutImageView.swift in Sources */, D7013E0126C3B116006F58E3 /* NSColor+.swift in Sources */, D7315ED0215ECF3000AB49D4 /* EditorView.swift in Sources */, D7E81C3A1F925B5F00416A91 /* NotesTableView.swift in Sources */, D7A9C1D72910784400905619 /* Project+Git.swift in Sources */, D7153DFE2285A93300A2C20F /* AboutWindowController.swift in Sources */, 11F2D4F52F104322002E4E47 /* Project+Date.swift in Sources */, D74D47A0256DF2EB00D97647 /* FSNTextAttachmentCell.swift in Sources */, 11BD8F9A2EDDF336000673A7 /* SolarizedDark.swift in Sources */, D77CC042216A608500582B97 /* EditorScrollView.swift in Sources */, 110BE0222EE8C53C00C5E456 /* TypeScript.swift in Sources */, 110BE0132EE86B4A00C5E456 /* Clojure.swift in Sources */, 11A6A8FB2EF06B9D005D000A /* EditTextView+MoveLines.swift in Sources */, D7767C7F234E47B9006A0716 /* Markdown.swift in Sources */, D7E81C3D1F925B5F00416A91 /* SandboxBookmark.swift in Sources */, D7487F922173503C00D09383 /* AttributedBox.swift in Sources */, D730BD3D222DB9FC00E69C93 /* NameHelper.swift in Sources */, D7153E062285C09C00A2C20F /* AboutViewController.swift in Sources */, D73BCCBA28EB5EC3008B3BBC /* Tag.swift in Sources */, 11BF066C2EE331B5006C7336 /* Scala.swift in Sources */, 11D6C0F42EE22D3B006017F0 /* Dart.swift in Sources */, D768D75D245ED6470028F344 /* VerticallyAlignedTextFieldCell.swift in Sources */, D7F5C0FC223ED58F0038F172 /* PreferencesAdvancedViewController.swift in Sources */, D7CC44C22A1E5E4F00743857 /* ViewController+WebApi.swift in Sources */, D7E81C3F1F925B5F00416A91 /* NSFont+.swift in Sources */, 110BE0162EE8C1C900C5E456 /* Html.swift in Sources */, 11D6C0DF2EE22B77006017F0 /* Csharp.swift in Sources */, 11F389572EEA10930008EC18 /* Mermaid.swift in Sources */, D7E81C411F925B5F00416A91 /* SearchTextField.swift in Sources */, 11F018AD2EF7E78600F07580 /* MPreviewFindPanel.swift in Sources */, D7F5C0F3223ED0C00038F172 /* PreferencesUserInterfaceViewController.swift in Sources */, D73BCC9F28EB5EC3008B3BBC /* Error.swift in Sources */, D768D758245E86670028F344 /* NSAttributedString+.swift in Sources */, D73BCCFF28EB5EC4008B3BBC /* Repository+Open.swift in Sources */, D73BCCF928EB5EC4008B3BBC /* RepositoryManager.swift in Sources */, D7E81C421F925B5F00416A91 /* MainWindowController.swift in Sources */, D71B9D832868658100D2F323 /* EditorViewController.swift in Sources */, D73BCCED28EB5EC4008B3BBC /* Head.swift in Sources */, 11D6C0F02EE22CBC006017F0 /* Matlab.swift in Sources */, D73BCCD828EB5EC4008B3BBC /* Statuses.swift in Sources */, D77A6F8028B11496006A0353 /* PreferencesWebViewController.swift in Sources */, D72DAF0929B27D75001243BB /* ProjectSettings.swift in Sources */, D7F5C0F6223ED5620038F172 /* PreferencesEditorViewController.swift in Sources */, D73BCCA528EB5EC3008B3BBC /* Progress.swift in Sources */, 113685622EC869950033767F /* URL+Image.swift in Sources */, 11D943202E643F250010CC2B /* Swift.swift in Sources */, 11BF06802EE4968C006C7336 /* Erlang.swift in Sources */, D73BCCB728EB5EC3008B3BBC /* Tags.swift in Sources */, D738356E2242871400B260DD /* MasterPasswordViewController.swift in Sources */, 11BF066F2EE33201006C7336 /* Bash.swift in Sources */, D7C1C99B235606CB0021A32D /* SidebarHeaderCellView.swift in Sources */, D7104A65230BD8C500B6D8EE /* SortDirection.swift in Sources */, D75627CF26D1165A000AF6EA /* ImageFormat.swift in Sources */, 11BD8FA62EDE0683000673A7 /* Theme.swift in Sources */, 11D6C0DB2EE22B0F006017F0 /* Rust.swift in Sources */, D7E81C441F925B5F00416A91 /* NoteCellView.swift in Sources */, D78678CC2093AE10001A6620 /* UndoData.swift in Sources */, D7E32C2D28F8D0750048614B /* StaticSshKeyDelegate.swift in Sources */, 11F3F4862EDB0E2A00435CBF /* String+.swift in Sources */, D7C9029323547A1E00A89BD8 /* FSTag.swift in Sources */, D730BD2F222DABA100E69C93 /* NoteContainer.swift in Sources */, D730BD36222DB11E00E69C93 /* TextBundleInfo.swift in Sources */, 11F1771B2EF1E93500CC566F /* ViewController+Menu.swift in Sources */, D7E81C461F925B5F00416A91 /* Note.swift in Sources */, D73BCCA828EB5EC3008B3BBC /* Blob.swift in Sources */, D7153E0A2285EC6100A2C20F /* TitleTextField.swift in Sources */, D7D372F5207B5B0F00AFBD9F /* SidebarNotesView.swift in Sources */, 1161828E2E637E31005B5EE0 /* SwiftHighlighter.swift in Sources */, 110E40A22EA0150300C62F49 /* NSTextCheckingResult+.swift in Sources */, 11D6C1012EE22E4A006017F0 /* Scratch.swift in Sources */, D73BCC9328EB5EC3008B3BBC /* Branches.swift in Sources */, 11D6C0F72EE22D78006017F0 /* Vb.swift in Sources */, D79798A329C0FE6A00B9A878 /* SettingsViewController.swift in Sources */, D79651B22517741400333AD4 /* ProgressState.swift in Sources */, D792DD9727A6D71C006ADC01 /* String+Punycode.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ D70B1FA52920DA6A003923DC /* PBXTargetDependency */ = { isa = PBXTargetDependency; productRef = D70B1FA42920DA6A003923DC /* SoulverCore */; }; D70B1FA72920DA73003923DC /* PBXTargetDependency */ = { isa = PBXTargetDependency; productRef = D70B1FA62920DA73003923DC /* SoulverCore */; }; D75F333F205EC34800CC887E /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = D75F3335205EC34800CC887E /* FSNotes iOS Share Extension */; targetProxy = D75F333E205EC34800CC887E /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ D7793C781F211C6000CA39B7 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( D7793C791F211C6000CA39B7 /* Base */, 42E001CD2ADAC2930099E7AD /* mul */, ); name = Main.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ D75F3342205EC34800CC887E /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 484D580095FCD450AE46554D /* Pods-FSNotes iOS Share Extension.debug.xcconfig */; buildSettings = { CODE_SIGN_ENTITLEMENTS = "FSNotes iOS Share/FSNotes iOS Share.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 332; DEVELOPMENT_TEAM = 866P6MTE92; INFOPLIST_FILE = "FSNotes iOS Share/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); MARKETING_VERSION = 7.1.2; PRODUCT_BUNDLE_IDENTIFIER = "co.fluder.mobile.FSNotes-iOS.FSNotes-iOS-Share"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = SHARE_EXT; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; D75F3343205EC34800CC887E /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 781ADC8297B1AC61D8E278F6 /* Pods-FSNotes iOS Share Extension.release.xcconfig */; buildSettings = { CODE_SIGN_ENTITLEMENTS = "FSNotes iOS Share/FSNotes iOS Share.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 332; DEVELOPMENT_TEAM = 866P6MTE92; ENABLE_NS_ASSERTIONS = NO; INFOPLIST_FILE = "FSNotes iOS Share/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); MARKETING_VERSION = 7.1.2; PRODUCT_BUNDLE_IDENTIFIER = "co.fluder.mobile.FSNotes-iOS.FSNotes-iOS-Share"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = SHARE_EXT; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; D7679399201F21F5000F7BBF /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = E73B6900CD2E14B0D5E51599 /* Pods-FSNotes iOS.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = ""; ASSETCATALOG_COMPILER_APPICON_NAME = modern; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = "FSNotes iOS/FSNotes iOS.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 332; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 866P6MTE92; INFOPLIST_FILE = "FSNotes iOS/Info.plist"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 7.1.2; PRODUCT_BUNDLE_IDENTIFIER = "co.fluder.mobile.FSNotes-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "CLOUD_RELATED_BLOCK IOS_APP"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_ENABLE_BATCH_MODE = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; D767939A201F21F5000F7BBF /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 4796D64E77383E6A5A3F900B /* Pods-FSNotes iOS.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = ""; ASSETCATALOG_COMPILER_APPICON_NAME = modern; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CFNETWORK_DIAGNOSTICS = 1; CODE_SIGN_ENTITLEMENTS = "FSNotes iOS/FSNotes iOS.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 332; DEVELOPMENT_TEAM = 866P6MTE92; ENABLE_NS_ASSERTIONS = NO; INFOPLIST_FILE = "FSNotes iOS/Info.plist"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 7.1.2; PRODUCT_BUNDLE_IDENTIFIER = "co.fluder.mobile.FSNotes-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "CLOUD_RELATED_BLOCK IOS_APP"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_ENABLE_BATCH_MODE = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; D7793C931F211C6000CA39B7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 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_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_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; 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_DEPRECATED_FUNCTIONS = 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 = 12.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; }; name = Debug; }; D7793C941F211C6000CA39B7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 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_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_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; "GCC_PREPROCESSOR_DEFINITIONS[arch=x86_64]" = ""; "GCC_PREPROCESSOR_DEFINITIONS[sdk=macosx*]" = ""; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = 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 = 12.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; }; name = Release; }; D7793C961F211C6000CA39B7 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 365935BA74CEA7B5CAFAB536 /* Pods-FSNotes.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = FSNotes/FSNotes.entitlements; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = ""; EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_ON_DEMAND_RESOURCES = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", ); INFOPLIST_FILE = FSNotes/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 12.4; PRODUCT_BUNDLE_IDENTIFIER = co.fluder.FSNotes; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; VALIDATE_PRODUCT = YES; }; name = Debug; }; D7793C971F211C6000CA39B7 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9EA62EEDB6BE9BF8727E66E0 /* Pods-FSNotes.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = FSNotes/FSNotes.entitlements; CODE_SIGN_IDENTITY = "3rd Party Mac Developer Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEPLOYMENT_LOCATION = NO; DEVELOPMENT_TEAM = 866P6MTE92; EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES; ENABLE_ON_DEMAND_RESOURCES = NO; ENABLE_TESTABILITY = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", ); INFOPLIST_FILE = FSNotes/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 12.4; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = co.fluder.FSNotes; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = "MAS Distribution"; SDKROOT = macosx; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; VALIDATE_PRODUCT = YES; }; name = Release; }; D7E81C581F925B5F00416A91 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 62E7ACC8B47FFD05898BD354 /* Pods-FSNotes (iCloud).debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = modern; CODE_SIGN_ENTITLEMENTS = "FSNotes/FSNotes (CloudKit).entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 706; DEVELOPMENT_TEAM = 866P6MTE92; EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES; ENABLE_HARDENED_RUNTIME = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_ON_DEMAND_RESOURCES = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", ); INFOPLIST_FILE = FSNotes/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 12.4; MARKETING_VERSION = 7.1.1; OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = co.fluder.FSNotes; PRODUCT_NAME = FSNotes; PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = CLOUD_RELATED_BLOCK; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; VALIDATE_PRODUCT = YES; }; name = Debug; }; D7E81C591F925B5F00416A91 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = EF024607824ECBF0AE378D65 /* Pods-FSNotes (iCloud).release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = modern; CODE_SIGN_ENTITLEMENTS = "FSNotes/FSNotes (CloudKit).entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 706; DEPLOYMENT_LOCATION = NO; DEVELOPMENT_TEAM = 866P6MTE92; EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES; ENABLE_HARDENED_RUNTIME = YES; ENABLE_ON_DEMAND_RESOURCES = NO; ENABLE_TESTABILITY = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", ); INFOPLIST_FILE = FSNotes/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 12.4; MARKETING_VERSION = 7.1.1; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = co.fluder.FSNotes; PRODUCT_NAME = FSNotes; PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = CLOUD_RELATED_BLOCK; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; VALIDATE_PRODUCT = YES; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ D75F3341205EC34800CC887E /* Build configuration list for PBXNativeTarget "FSNotes iOS Share Extension" */ = { isa = XCConfigurationList; buildConfigurations = ( D75F3342205EC34800CC887E /* Debug */, D75F3343205EC34800CC887E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; D7679398201F21F5000F7BBF /* Build configuration list for PBXNativeTarget "FSNotes iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( D7679399201F21F5000F7BBF /* Debug */, D767939A201F21F5000F7BBF /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; D7793C6A1F211C6000CA39B7 /* Build configuration list for PBXProject "FSNotes" */ = { isa = XCConfigurationList; buildConfigurations = ( D7793C931F211C6000CA39B7 /* Debug */, D7793C941F211C6000CA39B7 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; D7793C951F211C6000CA39B7 /* Build configuration list for PBXNativeTarget "FSNotes" */ = { isa = XCConfigurationList; buildConfigurations = ( D7793C961F211C6000CA39B7 /* Debug */, D7793C971F211C6000CA39B7 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; D7E81C571F925B5F00416A91 /* Build configuration list for PBXNativeTarget "FSNotes (iCloud)" */ = { isa = XCConfigurationList; buildConfigurations = ( D7E81C581F925B5F00416A91 /* Debug */, D7E81C591F925B5F00416A91 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ D70B1FA12920D91A003923DC /* XCRemoteSwiftPackageReference "SoulverCore" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/soulverteam/SoulverCore"; requirement = { kind = upToNextMinorVersion; minimumVersion = 2.2.0; }; }; D7B4CC5428C7B8860046A25F /* XCRemoteSwiftPackageReference "swift-git" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/glushchenko/swift-git"; requirement = { kind = revision; revision = 7b2f3af96c6947308b4f0b4ae02302fd47a5eff2; }; }; D7D2F27C2B54BD42003DCA47 /* XCRemoteSwiftPackageReference "Shout" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/glushchenko/Shout"; requirement = { branch = master; kind = branch; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ D70B1FA42920DA6A003923DC /* SoulverCore */ = { isa = XCSwiftPackageProductDependency; package = D70B1FA12920D91A003923DC /* XCRemoteSwiftPackageReference "SoulverCore" */; productName = SoulverCore; }; D70B1FA62920DA73003923DC /* SoulverCore */ = { isa = XCSwiftPackageProductDependency; package = D70B1FA12920D91A003923DC /* XCRemoteSwiftPackageReference "SoulverCore" */; productName = SoulverCore; }; D70F830328CE858E004818C5 /* Git */ = { isa = XCSwiftPackageProductDependency; package = D7B4CC5428C7B8860046A25F /* XCRemoteSwiftPackageReference "swift-git" */; productName = Git; }; D70F830528CE8596004818C5 /* Git */ = { isa = XCSwiftPackageProductDependency; package = D7B4CC5428C7B8860046A25F /* XCRemoteSwiftPackageReference "swift-git" */; productName = Git; }; D7A9C1DA29107A0800905619 /* Git */ = { isa = XCSwiftPackageProductDependency; package = D7B4CC5428C7B8860046A25F /* XCRemoteSwiftPackageReference "swift-git" */; productName = Git; }; D7D2F27D2B54BD42003DCA47 /* Shout */ = { isa = XCSwiftPackageProductDependency; package = D7D2F27C2B54BD42003DCA47 /* XCRemoteSwiftPackageReference "Shout" */; productName = Shout; }; D7D2F27F2B54BDD4003DCA47 /* Shout */ = { isa = XCSwiftPackageProductDependency; package = D7D2F27C2B54BD42003DCA47 /* XCRemoteSwiftPackageReference "Shout" */; productName = Shout; }; D7D2F2812B54BE59003DCA47 /* Shout */ = { isa = XCSwiftPackageProductDependency; package = D7D2F27C2B54BD42003DCA47 /* XCRemoteSwiftPackageReference "Shout" */; productName = Shout; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = D7793C671F211C6000CA39B7 /* Project object */; } ================================================ FILE: FSNotes.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: FSNotes.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: FSNotes.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings ================================================ BuildSystemType Original ================================================ FILE: FSNotes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved ================================================ { "originHash" : "2d1aa1f63d518360e031de3fb441c9f901a838fdc4472b5c997db08bea05709c", "pins" : [ { "identity" : "bluesocket", "kind" : "remoteSourceControl", "location" : "https://github.com/IBM-Swift/BlueSocket", "state" : { "revision" : "7b23a867008e0027bfd6f4d398d44720707bc8ca", "version" : "2.0.4" } }, { "identity" : "libssh2prebuild", "kind" : "remoteSourceControl", "location" : "https://github.com/glushchenko/Libssh2Prebuild.git", "state" : { "revision" : "024c2cf73e0b47deeea92a1176998f48f98ff4df", "version" : "1.11.0-OpenSSL-1-1-1u" } }, { "identity" : "shout", "kind" : "remoteSourceControl", "location" : "https://github.com/glushchenko/Shout", "state" : { "branch" : "master", "revision" : "d733c3e6d3c3d7d44e08489e2dc21eff50a61a0e" } }, { "identity" : "swift-cgit2", "kind" : "remoteSourceControl", "location" : "https://github.com/glushchenko/swift-cgit2", "state" : { "revision" : "ab1a18d82002b185c1c73b0daaa4174bb6b4f29c", "version" : "1.2.2" } }, { "identity" : "swift-git", "kind" : "remoteSourceControl", "location" : "https://github.com/glushchenko/swift-git", "state" : { "revision" : "7b2f3af96c6947308b4f0b4ae02302fd47a5eff2" } }, { "identity" : "swift-system", "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-system", "state" : { "revision" : "39774ef16a6d91dee6f666b940e00ea202710cf7", "version" : "0.0.3" } } ], "version" : 3 } ================================================ FILE: FSNotes.xcodeproj/xcshareddata/xcschemes/FSNotes (iCloud).xcscheme ================================================ ================================================ FILE: FSNotes.xcodeproj/xcshareddata/xcschemes/FSNotes iOS Share Extension.xcscheme ================================================ ================================================ FILE: FSNotes.xcodeproj/xcshareddata/xcschemes/FSNotes iOS.xcscheme ================================================ ================================================ FILE: FSNotes.xcodeproj/xcshareddata/xcschemes/FSNotes.xcscheme ================================================ ================================================ FILE: FSNotes.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: FSNotesCore/Business/ApiResponse.swift ================================================ // // ApiResponse.swift // FSNotes // // Created by Oleksandr Hlushchenko on 24.05.2023. // Copyright © 2023 Oleksandr Hlushchenko. All rights reserved. // import Foundation struct APIResponse: Codable { var id: String? var error: String? } ================================================ FILE: FSNotesCore/Business/AppearanceType.swift ================================================ // // AppearanceType.swift // FSNotes // // Created by Oleksandr Glushchenko on 10/1/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation public enum AppearanceType: Int { case System = 0x00 case Light = 0x01 case Dark = 0x02 } ================================================ FILE: FSNotesCore/Business/AttributedBox.swift ================================================ // // AttributedBox.swift // FSNotes // // Created by Oleksandr Glushchenko on 8/30/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // #if os(iOS) import UIKit #else import Cocoa #endif class AttributedBox { public static func getChecked(clean: Bool = false) -> NSMutableAttributedString? { let checkboxText = getCleanChecked() if clean { return checkboxText } checkboxText.append(NSAttributedString(string: " ")) return checkboxText } public static func getUnChecked(clean: Bool = false) -> NSMutableAttributedString? { let checkboxText = getCleanUnchecked() if clean { return checkboxText } checkboxText.append(NSAttributedString(string: " ")) return checkboxText } public static func getCleanUnchecked() -> NSMutableAttributedString { let font = NotesTextProcessor.font let size = font.pointSize + 3 let image = getImage(name: "checkbox_empty") let attachment = NSTextAttachment() attachment.image = image attachment.bounds = CGRect(x: CGFloat(0), y: (font.capHeight - size) / 2, width: size, height: size) let checkboxText = NSMutableAttributedString(attributedString: NSAttributedString(attachment: attachment)) checkboxText.addAttribute(.todo, value: 0, range: NSRange(0..<1)) if #available(OSX 10.13, iOS 10.0, *) { } else { let offset = (font.capHeight - size) / 2 checkboxText.addAttribute(.baselineOffset, value: offset, range: NSRange(0..<1)) } let parStyle = NSMutableParagraphStyle() parStyle.lineSpacing = CGFloat(UserDefaultsManagement.editorLineSpacing) checkboxText.addAttribute(.paragraphStyle, value: parStyle, range: NSRange(0..<1)) return checkboxText } public static func getCleanChecked() -> NSMutableAttributedString { let font = NotesTextProcessor.font let size = font.pointSize + 3 let attachment = NSTextAttachment() let image = getImage(name: "checkbox") attachment.image = image attachment.bounds = CGRect(x: CGFloat(0), y: (font.capHeight - size) / 2, width: size, height: size) let checkboxText = NSMutableAttributedString(attributedString: NSAttributedString(attachment: attachment)) checkboxText.addAttribute(.todo, value: 1, range: NSRange(0..<1)) if #available(OSX 10.13, iOS 10.0, *) { } else { let offset = (font.capHeight - size) / 2 checkboxText.addAttribute(.baselineOffset, value: offset, range: NSRange(0..<1)) } let parStyle = NSMutableParagraphStyle() parStyle.lineSpacing = CGFloat(UserDefaultsManagement.editorLineSpacing) checkboxText.addAttribute(.paragraphStyle, value: parStyle, range: NSRange(0..<1)) return checkboxText } public static func getImage(name: String) -> Image { var name = name #if os(OSX) if name == "checkbox" { if #available(OSX 10.15, *) { name = "checkbox_new" } else { name = "checkbox_flipped" } } return NSImage(named: name)! #else return UIImage(named: name)! #endif } } ================================================ FILE: FSNotesCore/Business/FSTag.swift ================================================ // // Tag.swift // FSNotes // // Created by Олександр Глущенко on 14.10.2019. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Foundation class FSTag { public var name: String public var parent: FSTag? public var child = [FSTag]() init(name: String, parent: FSTag? = nil) { self.name = name self.parent = parent let tags = name.components(separatedBy: "/") if tags.count > 1, let parent = tags.first { addChild(name: tags.dropFirst().joined(separator: "/"), completion: {(_, _, _) in }) self.name = parent return } } public func load(name: String) { self.name = name let tags = name.components(separatedBy: "/") if tags.count > 1, let parent = tags.first { addChild(name: tags.dropFirst().joined(separator: "/"), completion: {(_, _, _) in }) self.name = parent return } } public func isExpandable() -> Bool { return child.count > 0 } public func addChild(name: String, completion: (_ tag: FSTag, _ isExist: Bool, _ position: Int) -> Void) { let tags = name.components(separatedBy: "/") if let index = child.firstIndex(where: { $0.name == tags.first }) { completion(child[index], true, index) } else { let newTag = FSTag(name: name, parent: self) let index = getChildPosition(for: newTag) child.insert(newTag, at: index) completion(newTag, false, index) } } public func getChildPosition(for tag: FSTag) -> Int { var tags = child tags.append(tag) let sorted = tags.sorted(by: { $0.name.lowercased() < $1.name.lowercased() }) if let index = sorted.firstIndex(where: { $0 === tag }) { return index } return 0 } public func indexOf(child tag: FSTag) -> Int? { return child.firstIndex(where: { $0 === tag }) } public func remove(by index: Int) { child.remove(at: index) } public func removeChild(tag: FSTag) { child.removeAll(where: { $0 === tag }) } public func removeParent() { parent = nil } public func get(name: String) -> FSTag? { var name = name let tags = name.components(separatedBy: "/") if tags.count > 1, let parent = tags.first { name = parent } return child.first(where: { $0.name == name }) } public func getName() -> String { return name } public func getFullName() -> String { if let parentTag = parent?.getFullName(), parentTag != "" { return "\(parentTag)/\(name)" } if name == NSLocalizedString("Tags", comment: "Sidebar label") { return String() } return "\(name)" } public func find(name: String) -> FSTag? { let tags = name.components(separatedBy: "/") let trimmed = tags.dropFirst().joined(separator: "/") if let child = get(name: trimmed) { if child.name == trimmed { return child } return child.find(name: trimmed) } return nil } public func isAlone() -> Bool { guard let parent = parent else { return false } if parent.child.count == 1 { return true } return false } public func getParent() -> FSTag? { return parent } public func hasOneChild() -> Bool { return child.count < 2 } public func removeAllChild() { if child.count < 2 { child.removeAll() } } public func getAllChild() -> [String] { var tags = [String]() tags.append(getFullName()) var queue = [FSTag]() queue.append(contentsOf: child) while queue.count > 0 { for item in queue { tags.append(item.getFullName()) if item.child.count > 0 { queue.append(contentsOf: item.child) } queue.removeAll(where: { $0 === item }) } if queue.count == 0 { break } } return tags } } ================================================ FILE: FSNotesCore/Business/ImageFormat.swift ================================================ // // ImageFormat.swift // // Created by Олександр Глущенко on 08.06.2020. // Copyright © 2020 Oleksandr Glushchenko. All rights reserved. // import Foundation enum ImageFormat: String { case png, jpg, gif, tiff, webp, heic, unknown } extension ImageFormat { static func get(from data: Data) -> ImageFormat { switch data[0] { case 0x89: return .png case 0xFF: return .jpg case 0x47: return .gif case 0x49, 0x4D: return .tiff case 0x52 where data.count >= 12: let subdata = data[0...11] if let dataString = String(data: subdata, encoding: .ascii), dataString.hasPrefix("RIFF"), dataString.hasSuffix("WEBP") { return .webp } case 0x00 where data.count >= 12 : let subdata = data[8...11] if let dataString = String(data: subdata, encoding: .ascii), Set(["heic", "heix", "hevc", "hevx"]).contains(dataString) ///OLD: "ftypheic", "ftypheix", "ftyphevc", "ftyphevx" { return .heic } default: break } return .unknown } var contentType: String { return "image/\(rawValue)" } } ================================================ FILE: FSNotesCore/Business/LanguageType.swift ================================================ // // LanguageType.swift // FSNotes // // Created by Oleksandr Glushchenko on 7/7/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation // // NoteFileType.swift // FSNotes // // Created by Oleksandr Glushchenko on 1/6/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation enum LanguageType: Int { case English = 0x00 case Russian = 0x01 case Ukrainian = 0x02 case Deutsch = 0x03 case Spanish = 0x04 case Arabic = 0x05 case Chinese = 0x06 case Korean = 0x07 case French = 0x08 case Dutch = 0x09 case Portuguese = 10 case Italian = 11 case Hebrew = 12 case Japanese = 13 case PtBr = 14 case Czech = 15 case Hindi = 16 case Turkish = 17 case ChineseTraditional var description: String { switch rawValue { case 0x00: return "English" case 0x01: return "Русский" case 0x02: return "Українська" case 0x03: return "Deutsch" case 0x04: return "Spanish" case 0x05: return "Arabic" case 0x06: return "Chinese (Simplified)" case 0x07: return "Korean" case 0x08: return "French" case 0x09: return "Dutch" case 10: return "Portuguese (Portugal)" case 11: return "Italian" case 12: return "Hebrew" case 13: return "Japanese" case 14: return "Portuguese (Brazilian)" case 15: return "Czech" case 16: return "Hindi" case 17: return "Turkish" case 18: return "Chinese (Traditional)" default: return "" } } var code: String { switch rawValue { case 0x00: return "en" case 0x01: return "ru" case 0x02: return "uk" case 0x03: return "de" case 0x04: return "es" case 0x05: return "ar" case 0x06: return "zh-Hans" case 0x07: return "ko" case 0x08: return "fr" case 0x09: return "nl-NL" case 10: return "pt-PT" case 11: return "it" case 12: return "he" case 13: return "ja" case 14: return "pt-BR" case 15: return "cs" case 16: return "hi" case 17: return "tr" case 18: return "zh-Hant" default: return "en" } } static func withName(rawValue: String) -> LanguageType { switch rawValue { case "English": return LanguageType.English case "Русский": return LanguageType.Russian case "Українська": return LanguageType.Ukrainian case "Deutsch": return LanguageType.Deutsch case "Spanish": return LanguageType.Spanish case "Arabic": return LanguageType.Arabic case "Chinese (Simplified)": return LanguageType.Chinese case "Chinese (Traditional)": return LanguageType.ChineseTraditional case "Korean": return LanguageType.Korean case "French": return LanguageType.French case "Dutch": return LanguageType.Dutch case "Portuguese (Portugal)": return LanguageType.Portuguese case "Italian": return LanguageType.Italian case "Hebrew": return LanguageType.Hebrew case "Japanese": return LanguageType.Japanese case "Portuguese (Brazilian)": return LanguageType.PtBr case "Czech": return LanguageType.Czech case "Hindi": return LanguageType.Hindi case "Turkish": return LanguageType.Turkish default: return LanguageType.English } } static func withCode(rawValue: String) -> Int { switch rawValue { case "en": return 0x00 case "ru": return 0x01 case "uk": return 0x02 case "de": return 0x03 case "es": return 0x04 case "ar": return 0x05 case "zh-Hans": return 0x06 case "zh-Hant": return 18 case "ko": return 0x07 case "fr": return 0x08 case "nl-NL": return 0x09 case "pt-PT": return 10 case "it": return 11 case "he": return 12 case "ja": return 13 case "pr-BR": return 14 case "cs": return 15 case "hi": return 16 case "tr": return 17 default: return 0x00 } } } ================================================ FILE: FSNotesCore/Business/Markdown.swift ================================================ // // Markdown.swift // FSNotes // // Created by Wonsup Yoon on 05/10/2019. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import libcmark_gfm func renderMarkdownHTML(markdown: String) -> String? { let markdown = markdown.replacingOccurrences(of: "{{TOC}}", with: "
") cmark_gfm_core_extensions_ensure_registered() guard let parser = cmark_parser_new(CMARK_OPT_FOOTNOTES) else { return nil } defer { cmark_parser_free(parser) } if let ext = cmark_find_syntax_extension("table") { cmark_parser_attach_syntax_extension(parser, ext) } if let ext = cmark_find_syntax_extension("autolink") { cmark_parser_attach_syntax_extension(parser, ext) } if let ext = cmark_find_syntax_extension("strikethrough") { cmark_parser_attach_syntax_extension(parser, ext) } if let ext = cmark_find_syntax_extension("tasklist") { cmark_parser_attach_syntax_extension(parser, ext) } cmark_parser_feed(parser, markdown, markdown.utf8.count) guard let node = cmark_parser_finish(parser) else { return nil } return String(cString: cmark_render_html(node, CMARK_OPT_HARDBREAKS | CMARK_OPT_UNSAFE, nil)) } func generateAlphabeticalString(length: Int) -> String { let alphabet = "abcdefghijklmnopqrstuvwxyz" var result = "@" let length = length - 2 for _ in 0.. Bool { let nsText = content.string as NSString var success = false FSParser.yamlBlockRegex.matches(nsText as String, range: NSRange(location: 0, length: nsText.length)) { match in guard let yamlRange = match?.range(at: 1), yamlRange.location == 0 else { return } let yamlText = nsText.substring(with: yamlRange) if let (title, preview) = self.loadYaml(components: yamlText.components(separatedBy: .newlines)) { self.title = title self.preview = preview success = true } } return success } func prepareComponents(from text: String) -> [String] { let trimmed = (text as NSString).trimmingCharacters(in: .whitespacesAndNewlines) return trimmed .components(separatedBy: .newlines) .compactMap { line -> String? in let cleaned = line.replacingOccurrences(of: "^#+", with: "", options: .regularExpression) return cleaned.isEmpty ? nil : cleaned } } func getNonEmptyLines() -> [String] { let nsText = content.string as NSString let length = nsText.length var lines: [String] = [] lines.reserveCapacity(10) var location = 0 while lines.count < 10 && location < length { let remainingRange = NSRange(location: location, length: length - location) let range = nsText.rangeOfCharacter(from: .newlines, range: remainingRange) if range.location != NSNotFound { let lineLength = range.location - location if lineLength > 0 { var line = nsText.substring(with: NSRange(location: location, length: lineLength)) if location == 0 { line = line.trimMDSyntax() } if !line.isEmpty { lines.append(line) } } location = range.location + range.length } else { let line = nsText.substring(from: location) if !line.isEmpty { lines.append(line) } break } } return lines } func loadYaml(components: [String]) -> (String, String)? { var tripleMinus = 0 var previewFragments = [String]() var titleRow = String() var previewRow = String() if components.first == "---", components.count > 1 { for string in components { if string == "---" { tripleMinus += 1 } let res = string.matchingStrings(regex: "^title: ([\"\'”“]?)([^\n]+)\\1$") if res.count > 0 { titleRow = res[0][2].trim() } if tripleMinus > 1 { previewFragments.append(string) } } } if previewFragments.count > 0 { let previewString = previewFragments .joined(separator: " ") .replacingOccurrences(of: "---", with: "") previewRow = getPreviewLabel(with: previewString) } if titleRow.count > 0 { return (titleRow, previewRow) } return nil } func loadTitleFromFileName() { let fileName = url.deletingPathExtension().pathComponents.last! .replacingOccurrences(of: ":", with: "") .replacingOccurrences(of: "/", with: "") self.title = fileName } } ================================================ FILE: FSNotesCore/Business/Note.swift ================================================ // // NoteMO+CoreDataClass.swift // FSNotes // // Created by Oleksandr Glushchenko on 9/24/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // // import Foundation import RNCryptor import SSZipArchive import LocalAuthentication public class Note: NSObject { @objc var title: String = "" var project: Project var container: NoteContainer = .none var type: NoteType = .Markdown var url: URL var content: NSMutableAttributedString = NSMutableAttributedString() var creationDate: Date? = Date() let dateFormatter = DateFormatter() let undoManager = UndoManager() public var tags = [String]() public var originalExtension: String? public var isBlocked: Bool = false /* Filename with extension ie "example.textbundle" */ public var name = String() /* Filename "example" */ public var fileName = String() public var preview: String = "" public var isPinned: Bool = false public var modifiedLocalAt = Date() public var imageUrl: [URL]? public var attachments: [URL]? public var isParsed = false private var decryptedTemporarySrc: URL? public var isLoaded = false public var isLoadedFromCache = false public var password: String? public var cacheLock: Bool = false public var cacheHash: UInt64? public var uploadPath: String? public var apiId: String? public var previewState: Bool = false private var selectedRange: NSRange? public var contentOffset = CGPoint() public var contentOffsetWeb = CGPoint() public var scrollPosition: Int? public var scrollOffset: CGFloat? public var codeBlockRangesCache: [NSRange]? // Load exist init(url: URL, with project: Project, modified: Date? = nil, created: Date? = nil) { if let modified = modified { modifiedLocalAt = modified } if let created = created { creationDate = created } self.url = url.standardized self.project = project super.init() self.parseURL(loadProject: false) } // Make new init(name: String? = nil, project: Project? = nil, type: NoteType? = nil, cont: NoteContainer? = nil) { let project = project ?? Storage.shared().getDefault()! let name = name ?? String() self.project = project self.name = name self.container = cont ?? UserDefaultsManagement.fileContainer self.type = type ?? UserDefaultsManagement.fileFormat let ext = container == .none ? self.type.getExtension(for: container) : "textbundle" url = NameHelper.getUniqueFileName(name: name, project: project, ext: ext) super.init() self.parseURL() } init(meta: NoteMeta, project: Project) { isLoadedFromCache = true if meta.title.count > 0 || (meta.imageUrl != nil && meta.imageUrl!.count > 0) { isParsed = true } url = meta.url attachments = meta.attachments imageUrl = meta.imageUrl title = meta.title preview = meta.preview modifiedLocalAt = meta.modificationDate creationDate = meta.creationDate isPinned = meta.pinned tags = meta.tags selectedRange = meta.selectedRange self.project = project super.init() parseURL(loadProject: false) } public func fileSize(atPath path: String) -> Int64? { do { let attributes = try FileManager.default.attributesOfItem(atPath: path) if let fileSize = attributes[.size] as? Int64 { return fileSize } } catch { print("Error retrieving file size: \(error.localizedDescription)") } return nil } public func isValidForCaching() -> Bool { return isLoaded || title.count > 0 || isEncrypted() || imageUrl != nil } func getMeta() -> NoteMeta { let date = creationDate ?? Date() return NoteMeta( url: url, attachments: attachments, imageUrl: imageUrl, title: title, preview: preview, modificationDate: modifiedLocalAt, creationDate: date, pinned: isPinned, tags: tags, selectedRange: selectedRange ) } /// Important for decrypted temporary containers public func getURL() -> URL { if let url = self.decryptedTemporarySrc { return url } return self.url } public func loadProject() { let sharedStorage = Storage.shared() if let project = sharedStorage.getProjectByNote(url: url) { self.project = project } } public func forceLoad(skipCreateDate: Bool = false, loadTags: Bool = true) { invalidateCache() load(tags: loadTags) if !skipCreateDate { loadCreationDate() } loadModifiedLocalAt() } public func setCreationDate(string: String) -> Bool { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" let userDate = formatter.date(from: string) let attributes = [FileAttributeKey.creationDate: userDate] do { try FileManager.default.setAttributes(attributes as [FileAttributeKey : Any], ofItemAtPath: url.path) creationDate = userDate if isTextBundle() { writeTextBundleInfo(url: getURL()) } return true } catch { print(error) return false } } public func setCreationDate(date: Date) -> Bool { let attributes = [FileAttributeKey.creationDate: date] do { try FileManager.default.setAttributes(attributes as [FileAttributeKey : Any], ofItemAtPath: url.path) creationDate = date if isTextBundle() { writeTextBundleInfo(url: getURL()) } return true } catch { return false } } private func readTitleAndPreview() -> (String?, String?) { guard let fileHandle = FileHandle(forReadingAtPath: url.path) else { print("Can not open the file.") return (nil, nil) } defer { fileHandle.closeFile() } var saveChars = false var title = String() var preview = String() while let char = String(data: fileHandle.readData(ofLength: 1), encoding: .utf8) { if char == "\n" { if saveChars { preview += " " } else { saveChars = true } continue } if saveChars { preview += char if preview.count >= 100 { break } } else { title += char } } preview = preview.trimmingCharacters(in: .whitespacesAndNewlines) title = title.trimmingCharacters(in: .whitespacesAndNewlines) return (title, preview) } public func uiLoad() { if let size = fileSize(atPath: self.url.path), size > 100000 { loadFileName() let data = readTitleAndPreview() if let title = data.0 { self.title = title.trimMDSyntax() } if let preview = data.1 { self.preview = preview.trimMDSyntax() } return } load(tags: true) } func load(tags: Bool = true) { #if SHARE_EXT return #endif if let attributedString = getContent() { cacheHash = nil content = attributedString.loadAttachments(self) } loadFileName() loadPreviewInfo() if !isTrash() && tags { loadTags() } isLoaded = true } func reload() -> Bool { guard let modifiedAt = getFileModifiedDate() else { return false } if (modifiedAt != modifiedLocalAt) { if let attributedString = getContent() { cacheHash = nil content = attributedString.loadAttachments(self) cacheCodeBlocks() } loadModifiedLocalAt() return true } return false } public func forceReload() { if container != .encryptedTextPack, let attributedString = getContent() { cacheHash = nil content = attributedString.loadAttachments(self) } } public func loadModifiedLocalAt() { modifiedLocalAt = getFileModifiedDate() ?? Date.distantPast } public func loadCreationDate() { creationDate = getFileCreationDate() ?? Date.distantPast } public func isTextBundle() -> Bool { return (container == .textBundle || container == .textBundleV2) } public func isFullLoadedTextBundle() -> Bool { return getContentFileURL() != nil } public func getExtensionForContainer() -> String { return type.getExtension(for: container) } public func getFileModifiedDate() -> Date? { let url = getURL() if isUnlocked() { do { let attr = try FileManager.default.attributesOfItem(atPath: self.url.path) return attr[FileAttributeKey.modificationDate] as? Date } catch {/*_*/} } if UserDefaultsManagement.useTextBundleMetaToStoreDates && isTextBundle() { let textBundleURL = url let json = textBundleURL.appendingPathComponent("info.json") if let jsonData = try? Data(contentsOf: json), let info = try? JSONDecoder().decode(TextBundleInfo.self, from: jsonData), let modified = info.modified { return Date(timeIntervalSince1970: TimeInterval(modified)) } } if let contentUrl = getContentFileURL() { do { let attr = try FileManager.default.attributesOfItem(atPath: contentUrl.path) return attr[FileAttributeKey.modificationDate] as? Date } catch { NSLog("Note modification date load error: \(error.localizedDescription)") } } return (try? url.resourceValues(forKeys: [.contentModificationDateKey]))? .contentModificationDate } public func getFileCreationDate() -> Date? { let url = getURL() if isUnlocked() { do { let attr = try FileManager.default.attributesOfItem(atPath: self.url.path) return attr[FileAttributeKey.creationDate] as? Date } catch {/*_*/} } if UserDefaultsManagement.useTextBundleMetaToStoreDates && isTextBundle() { let textBundleURL = url let json = textBundleURL.appendingPathComponent("info.json") if let jsonData = try? Data(contentsOf: json), let info = try? JSONDecoder().decode(TextBundleInfo.self, from: jsonData), let created = info.created { return Date(timeIntervalSince1970: TimeInterval(created)) } } if let contentUrl = getContentFileURL() { do { let attr = try FileManager.default.attributesOfItem(atPath: contentUrl.path) return attr[FileAttributeKey.creationDate] as? Date } catch { NSLog("Note creation date load error: \(error.localizedDescription)") } } return (try? url.resourceValues(forKeys: [.creationDateKey]))? .creationDate } func move(to: URL, project: Project? = nil, forceRewrite: Bool = false) -> Bool { let sharedStorage = Storage.shared() do { var destination = to if FileManager.default.fileExists(atPath: to.path) && !forceRewrite { guard let project = project ?? sharedStorage.getProjectByNote(url: to) else { return false } let ext = url.pathExtension destination = NameHelper.getUniqueFileName(name: title, project: project, ext: ext) } try FileManager.default.moveItem(at: url, to: destination) removeCacheForPreviewImages() #if os(OSX) let restorePin = isPinned if isPinned { removePin() } overwrite(url: destination) if restorePin { addPin() } #endif NSLog("File moved from \"\(url.deletingPathExtension().lastPathComponent)\" to \"\(destination.deletingPathExtension().lastPathComponent)\"") } catch { Swift.print(error) return false } return true } func getNewURL(name: String) -> URL { let escapedName = name .replacingOccurrences(of: ":", with: "") .replacingOccurrences(of: "/", with: "") var newUrl = url.deletingLastPathComponent() newUrl.appendPathComponent(escapedName + "." + url.pathExtension) return newUrl } public func remove() { if !isTrash() && !isEmpty() { let src = url if let trashURLs = removeFile() { let dst = trashURLs[0] self.url = dst parseURL() #if IOS_APP moveHistory(src: src, dst: dst) #endif } } else { _ = removeFile() if self.isPinned { removePin() } #if IOS_APP dropRevisions() #endif } } public func isEmpty() -> Bool { return content.length == 0 && !isEncrypted() } #if os(iOS) // Return URL moved in func removeFile(completely: Bool = false) -> Array? { if FileManager.default.fileExists(atPath: url.path) { if isTrash() || completely || isEmpty() { try? FileManager.default.removeItem(at: url) if type == .Markdown && container == .none { let urls = content.getImagesAndFiles() for url in urls { try? FileManager.default.removeItem(at: url.url) } } return nil } guard let trashUrl = getDefaultTrashURL() else { print("Trash not found") var resultingItemUrl: NSURL? if #available(iOS 11.0, *) { if let trash = Storage.shared().getDefaultTrash() { moveImages(to: trash) } try? FileManager.default.trashItem(at: url, resultingItemURL: &resultingItemUrl) if let result = resultingItemUrl, let path = result.path { return [URL(fileURLWithPath: path), url] } } return nil } var trashUrlTo = trashUrl.appendingPathComponent(name) if FileManager.default.fileExists(atPath: trashUrlTo.path) { let reserveName = "\(Int(Date().timeIntervalSince1970)) \(name)" trashUrlTo = trashUrl.appendingPathComponent(reserveName) } print("Note moved in custom Trash folder") if let trash = Storage.shared().getDefaultTrash() { moveImages(to: trash) } try? FileManager.default.moveItem(at: url, to: trashUrlTo) return [trashUrlTo, url] } return nil } #endif #if os(OSX) func removeFile(completely: Bool = false) -> Array? { guard FileManager.default.fileExists(atPath: url.path) else { return nil } if isTrash() || completely { do { try FileManager.default.removeItem(at: url) } catch let error as NSError { Swift.print("Remove file error: \(error.localizedDescription)") Swift.print("Error details: \(error.userInfo)") } if type == .Markdown && container == .none { let urls = content.getImagesAndFiles() for url in urls { try? FileManager.default.removeItem(at: url.url) } } return nil } do { guard let dst = Storage.shared().trashItem(url: url) else { var resultingItemUrl: NSURL? try FileManager.default.trashItem(at: url, resultingItemURL: &resultingItemUrl) guard let dst = resultingItemUrl else { return nil } let originalURL = url overwrite(url: dst as URL) return [self.url, originalURL] } if let trash = Storage.shared().getDefaultTrash() { moveImages(to: trash) } try FileManager.default.moveItem(at: url, to: dst) let originalURL = url overwrite(url: dst) return [self.url, originalURL] } catch { print("Trash error: \(error)") } return nil } #endif public func getAttachPrefix(url: URL? = nil) -> String { if let url = url, !url.isImage { return "files/" } return "i/" } public func move(from imageURL: URL, imagePath: String, to project: Project, copy: Bool = false) { let dstPrefix = getAttachPrefix(url: imageURL) let dest = project.url.appendingPathComponent(dstPrefix, isDirectory: true) if !FileManager.default.fileExists(atPath: dest.path) { try? FileManager.default.createDirectory(at: dest, withIntermediateDirectories: false, attributes: nil) if let data = "true".data(using: .utf8) { try? dest.setExtendedAttribute(data: data, forName: "es.fsnot.hidden.dir") } } do { if copy { try FileManager.default.copyItem(at: imageURL, to: dest) } else { try FileManager.default.moveItem(at: imageURL, to: dest) } } catch { if let fileName = ImagesProcessor.getFileName(from: imageURL, to: dest, ext: imageURL.pathExtension) { let dest = dest.appendingPathComponent(fileName) if copy { try? FileManager.default.copyItem(at: imageURL, to: dest) } else { try? FileManager.default.moveItem(at: imageURL, to: dest) } let prefix = "](" let postfix = ")" let imagePath = imagePath.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? imagePath let find = prefix + imagePath + postfix let replace = prefix + dstPrefix + fileName + postfix guard find != replace else { return } while content.mutableString.contains(find) { let range = content.mutableString.range(of: find) content.replaceCharacters(in: range, with: replace) } } } } public func moveImages(to project: Project) { if type == .Markdown && container == .none { let imagesMeta = content.getImagesAndFiles() for imageMeta in imagesMeta { let imagePath = project.url.appendingPathComponent(imageMeta.path).path project.storage.hideImages(directory: imagePath, srcPath: imagePath) // Copy if image used more then one time on project let copy = self.project.countNotes(contains: imageMeta.url) > 0 move(from: imageMeta.url, imagePath: imageMeta.path, to: project, copy: copy) } if imagesMeta.count > 0 { if save() { Storage.shared().add(self) } } } } private func getDefaultTrashURL() -> URL? { if let url = Storage.shared().getDefaultTrash()?.url { return url } return nil } public func getPreviewLabel(with text: String? = nil) -> String { var preview: String = "" let content = text ?? self.content.string let length = text?.count ?? self.content.string.count if length > 250 { if text == nil { let startIndex = content.index((content.startIndex), offsetBy: 0) let endIndex = content.index((content.startIndex), offsetBy: 250) preview = String(content[startIndex...endIndex]) } else { preview = String(content.prefix(250)) } } else { preview = content } preview = preview.replacingOccurrences(of: "\n", with: " ") if ( UserDefaultsManagement.horizontalOrientation && content.hasPrefix(" – ") == false ) { preview = " – " + preview } preview = preview.condenseWhitespace() if preview.starts(with: "![") { return "" } return preview } @objc func getDateForLabel() -> String { guard !UserDefaultsManagement.hideDate else { return String() } let date = self.project.storage.getSortByState() == .creationDate ? creationDate : modifiedLocalAt guard let date = date else { return String() } if NSCalendar.current.isDateInToday(date) { return dateFormatter.formatTimeForDisplay(date) } else { return dateFormatter.formatDateForDisplay(date) } } @objc func getCreationDateForLabel() -> String? { guard let creationDate = self.creationDate else { return nil } guard !UserDefaultsManagement.hideDate else { return nil } let calendar = NSCalendar.current if calendar.isDateInToday(creationDate) { return dateFormatter.formatTimeForDisplay(creationDate) } else { return dateFormatter.formatDateForDisplay(creationDate) } } func getContent() -> NSMutableAttributedString? { guard container != .encryptedTextPack, let url = getContentFileURL() else { return nil } do { return try NSMutableAttributedString(url: url, options: [ .documentType : NSAttributedString.DocumentType.plain, .characterEncoding : NSNumber(value: String.Encoding.utf8.rawValue) ], documentAttributes: nil) } catch { if let data = try? Data(contentsOf: url) { let encoding = NSString.stringEncoding(for: data, encodingOptions: nil, convertedString: nil, usedLossyConversion: nil) return try? NSMutableAttributedString(url: url, options: [ .documentType : NSAttributedString.DocumentType.plain, .characterEncoding : NSNumber(value: encoding) ], documentAttributes: nil) } } return nil } func isMarkdown() -> Bool { return type == .Markdown } func addPin(cloudSave: Bool = true) { isPinned = true if cloudSave { Storage.shared().saveCloudPins() } } func removePin(cloudSave: Bool = true) { if isPinned { isPinned = false if cloudSave { Storage.shared().saveCloudPins() } } } func togglePin() { if !isPinned { addPin() } else { removePin() } } func cleanMetaData(content: String) -> String { var extractedTitle = String() var author = String() var date = String() if (content.hasPrefix("---\n")) { var list = content.components(separatedBy: "---") if (list.count > 2) { let headerList = list[1].components(separatedBy: "\n") for header in headerList { if header.hasPrefix("title:") { extractedTitle = header.replacingOccurrences(of: "title:", with: "").trim() if extractedTitle.hasPrefix("\"") && extractedTitle.hasSuffix("\""){ extractedTitle = String(extractedTitle.dropFirst(1)) extractedTitle = String(extractedTitle.dropLast(1)) } } if header.hasPrefix("author:") { author = header.replacingOccurrences(of: "author:", with: "").trim() if author.hasPrefix("\"") && author.hasSuffix("\""){ author = String(author.dropFirst(1)) author = String(author.dropLast(1)) } } if header.hasPrefix("date:") { date = header.replacingOccurrences(of: "date:", with: "").trim() if date.hasPrefix("\"") && date.hasSuffix("\""){ date = String(date.dropFirst(1)) date = String(date.dropLast(1)) } } } list.removeSubrange(Range(0...1)) var result = String() if (extractedTitle.count > 0) { result = "

" + extractedTitle + "

\n\n" } if (author.count > 0) { result += "_" + author + "_\n\n" } if (date.count > 0) { result += "_" + date + "_\n\n" } if result.count > 0 { result += "
\n\n" } result += list.joined() return result } } return content } func getPrettifiedContent() -> String { #if IOS_APP || os(OSX) let mutable = NotesTextProcessor.convertAppTags(in: self.content.unloadAttachments(), codeBlockRanges: codeBlockRangesCache) let content = NotesTextProcessor.convertAppLinks(in: mutable, codeBlockRanges: codeBlockRangesCache) let result = cleanMetaData(content: content.string) .replacingOccurrences(of: "\n---\n", with: "\n
\n") return result #else return cleanMetaData(content: self.content.string) #endif } public func overwrite(url: URL) { self.url = url parseURL() } func parseURL(loadProject: Bool = true) { if (url.pathComponents.count > 0) { container = .withExt(rawValue: url.pathExtension) name = url.lastPathComponent if isTextBundle() { type = .Markdown container = .textBundle let infoUrl = url.appendingPathComponent("info.json") if FileManager.default.fileExists(atPath: infoUrl.path) { do { let jsonData = try Data(contentsOf: infoUrl) let info = try JSONDecoder().decode(TextBundleInfo.self, from: jsonData) if info.version == 0x02 { type = NoteType.withUTI(rawValue: info.type) container = .textBundleV2 originalExtension = info.flatExtension if UserDefaultsManagement.useTextBundleMetaToStoreDates { if let created = info.created { creationDate = Date(timeIntervalSince1970: TimeInterval(created)) } if let modified = info.modified { modifiedLocalAt = Date(timeIntervalSince1970: TimeInterval(modified)) } } } } catch { print("TB loading error \(error)") } } } if container == .none { type = .withExt(rawValue: url.pathExtension) } loadTitle() loadFileName() } if loadProject { self.loadProject() } } private func loadTitle() { if !project.settings.isFirstLineAsTitle() { title = url .deletingPathExtension() .pathComponents .last! .replacingOccurrences(of: ":", with: "") .replacingOccurrences(of: "/", with: "") } } private func loadFileName() { fileName = url.deletingPathExtension().lastPathComponent .replacingOccurrences(of: ":", with: "") .replacingOccurrences(of: "/", with: "") } public func getFileName() -> String { return fileName } public func save(attributed: NSAttributedString) { if container == .encryptedTextPack { return } guard let copy = attributed.copy() as? NSAttributedString else { return } modifiedLocalAt = Date() let operation = BlockOperation() operation.addExecutionBlock { [weak self] in guard let self = self else { return } if operation.isCancelled { return } let mutable = NSMutableAttributedString(attributedString: copy) self.save(content: mutable) usleep(1000000) if !operation.isCancelled { self.isBlocked = false } } Storage.shared().plainWriter.cancelAllOperations() Storage.shared().plainWriter.addOperation(operation) } public func save(content: NSMutableAttributedString) { self.content = content let copy = content.unloadAttachments() modifiedLocalAt = Date() if write(attributedString: copy) { Storage.shared().add(self) } } public func replace(tag: String, with string: String) { content.replaceTag(name: tag, with: string) _ = save() } public func delete(tag: String) { content.replaceTag(name: tag, with: "") _ = save() } public func save() -> Bool { let attributedString = self.content.unloadAttachments() return write(attributedString: attributedString) } private func write(attributedString: NSAttributedString) -> Bool { let url = getURL() let attributes = getFileAttributes() do { let fileWrapper = getFileWrapper(attributedString: attributedString) if isTextBundle() { let jsonUrl = url.appendingPathComponent("info.json") let fileExist = FileManager.default.fileExists(atPath: jsonUrl.path) if !fileExist { try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: false, attributes: nil) } if UserDefaultsManagement.useTextBundleMetaToStoreDates || !fileExist { self.writeTextBundleInfo(url: url) } } let contentSrc: URL? = getContentFileURL() let dst = contentSrc ?? getContentSaveURL() var originalContentsURL: URL? = nil if let contentSrc = contentSrc { originalContentsURL = contentSrc } try fileWrapper.write(to: dst, options: .atomic, originalContentsURL: originalContentsURL) try FileManager.default.setAttributes(attributes, ofItemAtPath: dst.path) if decryptedTemporarySrc != nil { Storage.shared().ciphertextWriter.cancelAllOperations() Storage.shared().ciphertextWriter.addOperation { guard Storage.shared().ciphertextWriter.operationCount == 1 else { return } self.writeEncrypted() } } } catch { NSLog("Write error \(error)") return false } return true } private func getContentSaveURL() -> URL { let url = getURL() if isTextBundle() { let ext = getExtensionForContainer() return url.appendingPathComponent("text.\(ext)") } return url } public func getContentFileURL() -> URL? { var url = getURL() if isTextBundle() { let ext = getExtensionForContainer() url = url.appendingPathComponent("text.\(ext)") if !FileManager.default.fileExists(atPath: url.path) { url = url.deletingLastPathComponent() if let dirList = try? FileManager.default.contentsOfDirectory(atPath: url.path), let first = dirList.first(where: { $0.starts(with: "text.") }) { url = url.appendingPathComponent(first) return url } return nil } return url } if FileManager.default.fileExists(atPath: url.path) { return url } return nil } private func getTextBundleJsonInfo() -> String { var data = [ "transient": "true", "type": "\"\(type.uti)\"", "creatorIdentifier": "\"co.fluder.fsnotes\"", "version": "2" ] if let originalExtension = originalExtension { data["flatExtension"] = "\"\(originalExtension)\"" } if UserDefaultsManagement.useTextBundleMetaToStoreDates { let creationDate = self.creationDate ?? Date() let modificationDate = self.modifiedLocalAt data["created"] = "\(Int(creationDate.timeIntervalSince1970))" data["modified"] = "\(Int(modificationDate.timeIntervalSince1970))" } var result = [String]() for key in [ "transient", "type", "creatorIdentifier", "version", "flatExtension", "created", "modified" ] { if let value = data[key] { result.append(" \"\(key)\" : \(value)") } } return "{\n" + result.joined(separator: ",\n") + "\n}" } private func getAssetsFileWrapper() -> FileWrapper { let wrapper = FileWrapper.init(directoryWithFileWrappers: [:]) wrapper.preferredFilename = "assets" do { let assets = url.appendingPathComponent("assets") var isDir = ObjCBool(false) if FileManager.default.fileExists(atPath: assets.path, isDirectory: &isDir) && isDir.boolValue { let files = try FileManager.default.contentsOfDirectory(atPath: assets.path) for file in files { let fileData = try Data(contentsOf: assets.appendingPathComponent(file)) wrapper.addRegularFile(withContents: fileData, preferredFilename: file) } } } catch { print(error) } return wrapper } private func writeTextBundleInfo(url: URL) { let url = url.appendingPathComponent("info.json") let info = getTextBundleJsonInfo() try? info.write(to: url, atomically: true, encoding: String.Encoding.utf8) } func getFileAttributes() -> [FileAttributeKey: Any] { let url = getContentFileURL() ?? url var attributes: [FileAttributeKey: Any] = [:] do { attributes = try FileManager.default.attributesOfItem(atPath: url.path) } catch {} attributes[.modificationDate] = modifiedLocalAt return attributes } func getFileWrapper(attributedString: NSAttributedString, forcePlain: Bool = false) -> FileWrapper { do { let range = NSRange(location: 0, length: attributedString.length) return try attributedString.fileWrapper(from: range, documentAttributes: [ .documentType : NSAttributedString.DocumentType.plain, .characterEncoding : NSNumber(value: String.Encoding.utf8.rawValue) ]) } catch { return FileWrapper() } } func getTitleWithoutLabel() -> String { let title = url.deletingPathExtension().pathComponents.last! .replacingOccurrences(of: ":", with: "") .replacingOccurrences(of: "/", with: "") if title.isValidUUID { return "" } return title } func isTrash() -> Bool { return project.isTrash } public func contains(terms: [S]) -> Bool { return name.localizedStandardContains(terms) || content.string.localizedStandardContains(terms) } public func loadTags() { if UserDefaultsManagement.inlineTags { _ = scanContentTags() } } public func scanContentTags() -> ([String], [String]) { var added = [String]() var removed = [String]() let matchingOptions = NSRegularExpression.MatchingOptions(rawValue: 0) let options: NSRegularExpression.Options = [ .allowCommentsAndWhitespace, .anchorsMatchLines ] var tags = [String]() do { let range = NSRange(location: 0, length: content.string.count) let re = try NSRegularExpression(pattern: FSParser.tagsPattern, options: options) re.enumerateMatches( in: content.string, options: matchingOptions, range: range, using: { (result, flags, stop) -> Void in guard var range = result?.range(at: 1) else { return } let cleanTag = content.mutableString.substring(with: range) range = NSRange(location: range.location - 1, length: range.length + 1) if let codeBlockRangesCache = codeBlockRangesCache { for codeRange in codeBlockRangesCache { if NSIntersectionRange(codeRange, range).length > 0 { return } } } let spanBlock = FSParser.getSpanCodeBlockRange(content: content, range: range) if spanBlock == nil && isValid(tag: cleanTag) { let parRange = content.mutableString.paragraphRange(for: range) let par = content.mutableString.substring(with: parRange) if par.starts(with: " ") || par.starts(with: "\t") { return } if cleanTag.last == "/" { tags.append(String(cleanTag.dropLast())) } else { tags.append(cleanTag) } } } ) } catch { print("Tags parsing: \(error)") } if tags.contains("notags") { removed = self.tags self.tags.removeAll() return (added, removed) } for noteTag in self.tags { if !tags.contains(noteTag) { removed.append(noteTag) } } for tag in tags { if !self.tags.contains(tag) { added.append(tag) } } self.tags = tags return (added, removed) } private var excludeRanges = [NSRange]() public func isValid(tag: String) -> Bool { if tag.isNumber { return false } if tag.isHexColor() { return false } return true } public func getAttachmentFileUrl(name: String) -> URL? { if name.count == 0 { return nil } if name.starts(with: "http://") || name.starts(with: "https://") { return URL(string: name) } if isEncrypted() && ( name.starts(with: "/i/") || name.starts(with: "i/") ) { return project.url.appendingPathComponent(name) } if isTextBundle() { return getURL().appendingPathComponent(name) } return project.url.appendingPathComponent(name) } #if os(OSX) public func getDupeName() -> String? { var url = self.url let ext = url.pathExtension url.deletePathExtension() var name = url.lastPathComponent url.deleteLastPathComponent() let regex = try? NSRegularExpression(pattern: "(.+)\\sCopy\\s(\\d)+$", options: .caseInsensitive) if let result = regex?.firstMatch(in: name, range: NSRange(0.. [URL] { var urls = [URL]() let range = NSRange(location: 0, length: content.length) content.enumerateAttribute(.attachment, in: range) { (value, vRange, _) in guard let meta = content.getMeta(at: vRange.location) else { return } if meta.url.isMedia { urls.append(meta.url) } } return urls } public func invalidateCache() { self.imageUrl = nil self.preview = String() self.title = String() self.isParsed = false } public func isEqualURL(url: URL) -> Bool { return url.path == self.url.path } public func append(string: NSMutableAttributedString) { content.append(string) } public func append(image data: Data, url: URL? = nil) { guard let path = ImagesProcessor.writeFile(data: data, url: url, note: self) else { return } var prefix = "\n\n" if content.length == 0 { prefix = String() } let markdown = NSMutableAttributedString(string: "\(prefix)![](\(path))") append(string: markdown) } @objc public func getName() -> String { if title.isValidUUID { return "Untitled Note" } return title } public func getCacheForPreviewImage(at url: URL) -> URL? { var temporary = URL(fileURLWithPath: NSTemporaryDirectory()) temporary.appendPathComponent("Preview") if let filePath = url.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) { return temporary.appendingPathComponent(filePath) } return nil } public func removeCacheForPreviewImages() { loadPreviewInfo() guard let imageURLs = imageUrl else { return } for url in imageURLs { if let imageURL = getCacheForPreviewImage(at: url) { if FileManager.default.fileExists(atPath: imageURL.path) { try? FileManager.default.removeItem(at: imageURL) } } } } private func convertFlatToTextBundle() -> URL { let temporary = URL(fileURLWithPath: NSTemporaryDirectory()) let temporaryProject = Project(storage: project.storage, url: temporary) let currentName = url.deletingPathExtension().lastPathComponent let note = Note(name: currentName, project: temporaryProject, type: type, cont: .textBundleV2) note.originalExtension = url.pathExtension note.content = content let imagesMeta = content.getImagesAndFiles() let mutableContent = content.unloadAttachments() // write textbundle body guard note.write(attributedString: mutableContent) else { return note.url } for imageMeta in imagesMeta { moveFilesFlatToAssets(attributedString: mutableContent, from: imageMeta.url, imagePath: imageMeta.path, to: note.url) } // write updated image pathes guard note.write(attributedString: mutableContent) else { return note.url } return note.url } private func convertTextBundleToFlat(name: String) { let textBundleURL = url let json = textBundleURL.appendingPathComponent("info.json") if let jsonData = try? Data(contentsOf: json), let info = try? JSONDecoder().decode(TextBundleInfo.self, from: jsonData) { let ext = NoteType.withUTI(rawValue: info.type).getExtension(for: .textBundleV2) let flatExtension = info.flatExtension ?? ext let fileName = "text.\(ext)" let uniqueURL = NameHelper.getUniqueFileName(name: name, project: project, ext: flatExtension) let flatURL = url.appendingPathComponent(fileName) url = uniqueURL type = .withExt(rawValue: flatExtension) container = .none try? FileManager.default.moveItem(at: flatURL, to: uniqueURL) moveFilesAssetsToFlat(src: textBundleURL, project: project) try? FileManager.default.removeItem(at: textBundleURL) } } private func moveFilesFlatToAssets(attributedString: NSMutableAttributedString, from imageURL: URL, imagePath: String, to dest: URL) { let dest = dest.appendingPathComponent("assets") guard let fileName = imageURL.lastPathComponent.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else { return } if !FileManager.default.fileExists(atPath: dest.path) { try? FileManager.default.createDirectory(at: dest, withIntermediateDirectories: false, attributes: nil) } do { try FileManager.default.moveItem(at: imageURL, to: dest.appendingPathComponent(fileName)) let prefix = "](" let postfix = ")" let find = prefix + imagePath + postfix let replace = prefix + "assets/" + imageURL.lastPathComponent + postfix guard find != replace else { return } while attributedString.mutableString.contains(find) { let range = attributedString.mutableString.range(of: find) attributedString.replaceCharacters(in: range, with: replace) } } catch { print("Enc error: \(error)") } } private func moveFilesAssetsToFlat(src: URL, project: Project) { let mutableContent = NSMutableAttributedString(attributedString: content).unloadAttachments() let imagesMeta = content.getImagesAndFiles() for imageMeta in imagesMeta { let fileName = imageMeta.url.lastPathComponent var dst: URL? var prefix = "files/" if imageMeta.url.isImage { prefix = "i/" } dst = project.url.appendingPathComponent(prefix + fileName) guard let moveTo = dst else { continue } let dstDir = project.url.appendingPathComponent(prefix) let moveFrom = src.appendingPathComponent("assets/" + fileName) do { if !FileManager.default.fileExists(atPath: dstDir.path) { try? FileManager.default.createDirectory(at: dstDir, withIntermediateDirectories: false, attributes: nil) } try FileManager.default.moveItem(at: moveFrom, to: moveTo) } catch { if let fileName = ImagesProcessor.getFileName(from: moveTo, to: dstDir, ext: moveTo.pathExtension) { let moveTo = dstDir.appendingPathComponent(fileName) try? FileManager.default.moveItem(at: moveFrom, to: moveTo) } } guard let escapedFileName = fileName.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else { continue } let find = "](assets/" + escapedFileName + ")" let replace = "](" + prefix + escapedFileName + ")" guard find != replace else { return } while mutableContent.mutableString.contains(find) { let range = mutableContent.mutableString.range(of: find) mutableContent.replaceCharacters(in: range, with: replace) } } content = mutableContent.loadAttachments(self) _ = save() } private func loadTextBundle() -> Bool { do { let url = getURL() let json = url.appendingPathComponent("info.json") let jsonData = try Data(contentsOf: json) let info = try JSONDecoder().decode(TextBundleInfo.self, from: jsonData) type = .withUTI(rawValue: info.type) if info.version == 1 { container = .textBundle return true } container = .textBundleV2 return true } catch { print("Can not load TextBundle: \(error)") } return false } private func writeEncrypted() { guard let baseTextPack = self.decryptedTemporarySrc else { return } let textPackURL = baseTextPack.appendingPathExtension("textpack") var password = self.password SSZipArchive.createZipFile(atPath: textPackURL.path, withContentsOfDirectory: baseTextPack.path) do { if password == nil { let item = KeychainPasswordItem(service: KeychainConfiguration.serviceName, account: "Master Password") password = try item.readPassword() } guard let unwrappedPassword = password else { return } let data = try Data(contentsOf: textPackURL) let encryptedData = RNCryptor.encrypt(data: data, withPassword: unwrappedPassword) try encryptedData.write(to: self.url) let attributes = getFileAttributes() try FileManager.default.setAttributes(attributes, ofItemAtPath: url.path) print("FSNotes successfully writed encrypted data for: \(title)") try FileManager.default.removeItem(at: textPackURL) } catch { return } } public func unLock(password: String) -> Bool { let sharedStorage = Storage.shared() do { let name = url.deletingPathExtension().lastPathComponent let data = try Data(contentsOf: url) guard let temporary = sharedStorage.makeTempEncryptionDirectory()?.appendingPathComponent(name) else { return false } let temporaryTextPack = temporary.appendingPathExtension("textpack") let temporaryTextBundle = temporary.appendingPathExtension("textbundle") let decryptedData = try RNCryptor.decrypt(data: data, withPassword: password) try decryptedData.write(to: temporaryTextPack) let successUnZip = SSZipArchive.unzipFile(atPath: temporaryTextPack.path, toDestination: temporaryTextBundle.path) try FileManager.default.removeItem(at: temporaryTextPack) guard successUnZip else { return false } self.decryptedTemporarySrc = temporaryTextBundle guard loadTextBundle() else { container = .encryptedTextPack return false } invalidateCache() load(tags: false) loadTitle() self.password = password return true } catch { print("Decryption error: \(error)") return false } } public func unEncrypt(password: String) -> Bool { let originalSrc = url do { let name = url.deletingPathExtension().lastPathComponent let data = try Data(contentsOf: url) let decryptedData = try RNCryptor.decrypt(data: data, withPassword: password) let textPackURL = getTempTextPackURL() try decryptedData.write(to: textPackURL) let newURL = project.url.appendingPathComponent(name + ".textbundle", isDirectory: false) url = newURL container = .textBundleV2 let successUnZip = SSZipArchive.unzipFile(atPath: textPackURL.path, toDestination: newURL.path) guard successUnZip else { url = originalSrc container = .encryptedTextPack return false } try FileManager.default.removeItem(at: textPackURL) try FileManager.default.removeItem(at: originalSrc) self.decryptedTemporarySrc = nil self.password = nil invalidateCache() load() parseURL() return true } catch { print("Decryption error: \(error)") return false } } public func unEncryptUnlocked() -> Bool { guard let decSrcUrl = decryptedTemporarySrc else { return false } let originalSrc = url do { let name = url.deletingPathExtension().lastPathComponent let newURL = project.url.appendingPathComponent(name).appendingPathExtension("textbundle") url = newURL container = .textBundleV2 try FileManager.default.removeItem(at: originalSrc) try FileManager.default.moveItem(at: decSrcUrl, to: newURL) self.decryptedTemporarySrc = nil load() parseURL() return true } catch { print("Encryption removing error: \(error)") return false } } public func encrypt(password: String) -> Bool { if container == .encryptedTextPack { return false } var temporaryFlatSrc: URL? let isContainer = isTextBundle() if !isContainer { temporaryFlatSrc = convertFlatToTextBundle() } let originalSrc = url let fileName = url.deletingPathExtension().lastPathComponent let baseTextPack = temporaryFlatSrc ?? url let textPackURL = getTempTextPackURL() SSZipArchive.createZipFile(atPath: textPackURL.path, withContentsOfDirectory: baseTextPack.path) do { if let tempURL = temporaryFlatSrc { try FileManager.default.removeItem(at: tempURL) } let encryptedURL = self.project.url .appendingPathComponent(fileName) .appendingPathExtension("etp") let data = try Data(contentsOf: textPackURL) let encrypted = RNCryptor.encrypt(data: data, withPassword: password) url = encryptedURL container = .encryptedTextPack parseURL() try encrypted.write(to: encryptedURL) try FileManager.default.removeItem(at: originalSrc) try FileManager.default.removeItem(at: textPackURL) cleanOut() removeTempContainer() invalidateCache() return true } catch { url = originalSrc parseURL() print("Encyption error: \(error) \(error.localizedDescription)") return false } } public func getTempTextPackURL() -> URL { let fileName = url.deletingPathExtension().lastPathComponent let textPackURL = URL(fileURLWithPath: NSTemporaryDirectory()) .appendingPathComponent(fileName, isDirectory: false) .appendingPathExtension("textpack") return textPackURL } public func encryptAndUnlock(password: String) { if encrypt(password: password) { _ = unLock(password: password) } } public func cleanOut() { isParsed = false imageUrl = nil cacheHash = nil content = NSMutableAttributedString(string: String()) preview = String() title = String() } private func removeTempContainer() { if let url = decryptedTemporarySrc { try? FileManager.default.removeItem(at: url) } } public func isUnlocked() -> Bool { return (decryptedTemporarySrc != nil) } public func isEncrypted() -> Bool { return (container == .encryptedTextPack || isUnlocked()) } public func isEncryptedAndLocked() -> Bool { return container == .encryptedTextPack && decryptedTemporarySrc == nil } public func lock() -> Bool { guard let temporaryURL = self.decryptedTemporarySrc else { return false } while true { if Storage.shared().ciphertextWriter.operationCount == 0 { print("Note \"\(title)\" successfully locked.") container = .encryptedTextPack cleanOut() parseURL() try? FileManager.default.removeItem(at: temporaryURL) self.decryptedTemporarySrc = nil self.password = nil return true } usleep(100000) } } public func showIconInList() -> Bool { return (isPinned || isEncrypted() || isPublished()) } public func getShortTitle() -> String { let fileName = getFileName() if fileName.isValidUUID { return "▽" } return fileName } public func getTitle() -> String? { if isEncrypted() && !isUnlocked() { return getFileName() } #if os(iOS) if !project.settings.isFirstLineAsTitle() { return getFileName() } #endif if title.count > 0 { if title.isValidUUID && project.settings.isFirstLineAsTitle() { return nil } if title.starts(with: "![") { return nil; } return title } if getFileName().isValidUUID { let previewCharsQty = preview.count if previewCharsQty > 0 { return "Untitled Note" } } return nil } public func rename(to name: String) { var name = name var i = 1 while project.fileExist(fileName: name, ext: url.pathExtension) { // disables renaming loop if fileName.startsWith(string: name) { return } let items = name.split(separator: " ") if let last = items.last, let position = Int(last) { let full = items.dropLast() name = full.joined(separator: " ") + " " + String(position + 1) i = position + 1 } else { name = name + " " + String(i) i += 1 } } let isPinned = self.isPinned let dst = getNewURL(name: name) removePin() if isEncrypted() { _ = lock() } if move(to: dst) { url = dst parseURL() } if isPinned { addPin() } } public func getCursorPosition() -> Int? { var position: Int? if let data = try? url.extendedAttribute(forName: "co.fluder.fsnotes.cursor") { position = data.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) -> Int in ptr.load(as: Int.self) } return position } return nil } public func addTag(_ name: String) { guard !tags.contains(name) else { return } let lastParRange = content.mutableString.paragraphRange(for: NSRange(location: content.length, length: 0)) let string = content.attributedSubstring(from: lastParRange).string.trim() if string.count != 0 && ( !string.starts(with: "#") || string.starts(with: "# ") ) { let newLine = NSAttributedString(string: "\n\n") content.append(newLine) } var prefix = String() if string.starts(with: "#") { prefix += " " } content.append(NSAttributedString(string: prefix + "#" + name)) if save() { Storage.shared().add(self) } } public func resetAttributesCache() { cacheHash = nil } public func getLatinName() -> String { let name = (self.fileName as NSString) .applyingTransform(.toLatin, reverse: false)? .applyingTransform(.stripDiacritics, reverse: false) ?? self.fileName return name.replacingOccurrences(of: " ", with: "_") } public func isPublished() -> Bool { return apiId != nil || uploadPath != nil } public func convertContainer(to: NoteContainer) { if to == .textBundleV2 { let tempUrl = convertFlatToTextBundle() let name = url.deletingPathExtension().lastPathComponent let uniqueURL = NameHelper.getUniqueFileName(name: name, project: project, ext: "textbundle") do { let oldUrl = url url = uniqueURL try FileManager.default.moveItem(at: tempUrl, to: uniqueURL) try FileManager.default.removeItem(at: oldUrl) } catch {/*_*/} } else { let name = url.deletingPathExtension().lastPathComponent convertTextBundleToFlat(name: name) } invalidateCache() load() parseURL() } public func getAutoRenameTitle() -> String? { if UserDefaultsManagement.naming != .autoRename && UserDefaultsManagement.naming != .autoRenameNew { return nil } if UserDefaultsManagement.naming == .autoRenameNew && isOlderThan30Seconds(from: creationDate) { return nil } if content.string.startsWith(string: "---") { loadPreviewInfo() } let title = title.trunc(length: 64) if fileName == title || title.count == 0 || isEncrypted() { return nil } if project.fileExist(fileName: title, ext: url.pathExtension) { return nil } return title } public func setSelectedRange(range: NSRange? = nil) { selectedRange = range } public func getSelectedRange() -> NSRange? { return selectedRange } public func setContentOffset(contentOffset: CGPoint) { self.contentOffset = contentOffset } public func getContentOffset() -> CGPoint { return contentOffset } public func getRelatedPath() -> String { return project.getNestedPath() + "/" + name } func isOlderThan30Seconds(from date: Date? = nil) -> Bool { guard let date = date else { return false } let thirtySecondsAgo = Date().addingTimeInterval(-30) return date < thirtySecondsAgo //Returns false if date is not older than 30 seconds } public func loadPreviewState() { previewState = project.settings.notesPreview.contains(name) } public func cacheCodeBlocks() { #if !SHARE_EXT let ranges = CodeBlockDetector.shared.findCodeBlocks(in: content) codeBlockRangesCache = ranges #endif } public func isInCodeBlockRange(range: NSRange) -> Bool { guard let codeBlockRangesCache = codeBlockRangesCache else { return false } for codeRange in codeBlockRangesCache { if NSIntersectionRange(range, codeRange).length > 0 { return true } } return false } public func save(data: Data, preferredName: String? = nil) -> (String, URL)? { // Get attach dir let attachDir = getAttachDirectory(data: data) // Create if not exist if !FileManager.default.fileExists(atPath: attachDir.path, isDirectory: nil) { try? FileManager.default.createDirectory(at: attachDir, withIntermediateDirectories: true, attributes: nil) } guard let fileName = getFileName(dst: attachDir, preferredName: preferredName) else { return nil } let fileUrl = attachDir.appendingPathComponent(fileName) do { try data.write(to: fileUrl, options: .atomic) } catch { print("Attachment error: \(error)") return nil } let lastTwo = fileUrl.deletingLastPathComponent().lastPathComponent + "/" + fileUrl.lastPathComponent return (lastTwo, fileUrl) } public func getAttachDirectory(data: Data) -> URL { if isTextBundle() { return getURL().appendingPathComponent("assets", isDirectory: true) } let prefix = data.getFileType() != .unknown ? "i" : "files" return project.url.appendingPathComponent(prefix, isDirectory: true) } public func getFileName(dst: URL, preferredName: String? = nil) -> String? { var name = preferredName ?? UUID().uuidString.lowercased() let ext = (name as NSString).pathExtension while true { let destination = dst.appendingPathComponent(name) let icloud = destination.appendingPathExtension("icloud") if FileManager.default.fileExists(atPath: destination.path) || FileManager.default.fileExists(atPath: icloud.path) { let newBase = UUID().uuidString.lowercased() if ext.isEmpty { name = newBase } else { name = "\(newBase).\(ext)" } continue } return name } } public func saveSimple() -> Bool { return write(attributedString: content) } #if os(macOS) public func cache() { if cacheLock { return } let hash = content.string.fnv1a cacheLock = true if let copy = content.mutableCopy() as? NSMutableAttributedString { NotesTextProcessor.highlight(attributedString: copy) cacheCodeBlocks() if content.string.fnv1a == copy.string.fnv1a { content = copy cacheHash = hash } } cacheLock = false } #endif } ================================================ FILE: FSNotesCore/Business/NoteAttachment.swift ================================================ // // ImageAttachment.swift // FSNotes // // Created by Oleksandr Glushchenko on 7/15/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation import AVKit #if os(OSX) import Cocoa typealias PlatformImage = NSImage #else import UIKit typealias PlatformImage = UIImage #endif // MARK: - NoteAttachment class NoteAttachment { // MARK: - Properties public let url: URL public var imageCache: URL? // MARK: - Constants private enum Constants { static let previewPrefix = "Preview" static let thumbnailPrefixMacOS = "ThumbnailsBig" static let thumbnailPrefixIOS = "ThumbnailsBigInline" static let fontFamily = "Avenir Next" static let fontNameIOS = "AvenirNext-BoldItalic" static let defaultFontSize: CGFloat = 14.0 static let fileSizeThreshold = 10000 static let bytesInMB: Double = 1_000_000 #if os(iOS) static let thumbnailPrefix = thumbnailPrefixIOS #else static let thumbnailPrefix = thumbnailPrefixMacOS #endif } // MARK: - Initialization init(url: URL) { self.url = url } // MARK: - Public Methods public func getImageText() -> String { let fileSize = url.fileSize let sizeTitle = formatFileSize(Int(fileSize)) return " \(url.lastPathComponent) – \(sizeTitle) 📎 " } public func getImageWidth(text: String) -> Double { let font = getAttachmentFont() return (text as NSString).size(withAttributes: [.font: font]).width } public func getAttachmentImage() -> PlatformImage? { let height = UserDefaultsManagement.noteFont.getAttachmentHeight() let text = getImageText() let width = getImageWidth(text: text) let imageSize = CGSize(width: width, height: height) return imageFromText(text: text, imageSize: imageSize) } // MARK: - Private Methods private func formatFileSize(_ size: Int) -> String { if size > Constants.fileSizeThreshold { let sizeInMB = Double(size) / Constants.bytesInMB return String(format: "%.2f MB", sizeInMB) } return "\(size) bytes" } private func getAttachmentFont() -> PlatformFont { #if os(OSX) let traits = NSFontTraitMask(rawValue: NSFontTraitMask.RawValue( NSFontBoldTrait | NSFontItalicTrait )) return NSFontManager().font( withFamily: Constants.fontFamily, traits: traits, weight: 1, size: CGFloat(UserDefaultsManagement.fontSize) ) ?? PlatformFont.systemFont(ofSize: Constants.defaultFontSize) #else return PlatformFont(name: Constants.fontNameIOS, size: CGFloat(UserDefaultsManagement.fontSize)) ?? PlatformFont.systemFont(ofSize: Constants.defaultFontSize) #endif } public func imageFromText(text: String, imageSize: CGSize) -> PlatformImage? { let font = getAttachmentFont() let attributes = createTextAttributes(font: font) let textSize = text.size(withAttributes: attributes) #if os(OSX) return createImageMacOS(text: text, imageSize: imageSize, attributes: attributes, textSize: textSize) #else return createImageIOS(text: text, imageSize: imageSize, attributes: attributes, textSize: textSize) #endif } private func createTextAttributes(font: PlatformFont) -> [NSAttributedString.Key: Any] { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .center #if os(OSX) let isDark = UserDataService.instance.isDark let textColor: PlatformColor = isDark ? .white : .black let backgroundColor: PlatformColor = isDark ? NSColor(red: 0.16, green: 0.17, blue: 0.18, alpha: 1.00) : .white #else let textColor = NotesTextProcessor.fontColor let backgroundColor = UIColor.dropDownColor #endif return [ .font: font, .foregroundColor: textColor, .backgroundColor: backgroundColor, .paragraphStyle: paragraphStyle ] } private func calculateCenteredRect(textSize: CGSize, containerSize: CGSize) -> CGRect { return CGRect( x: (containerSize.width - textSize.width) / 2.0, y: (containerSize.height - textSize.height) / 2.0, width: textSize.width, height: textSize.height ) } } // MARK: - Static Utility Methods (Platform-Independent) extension NoteAttachment { public static func getCacheUrl(from url: URL, prefix: String = Constants.previewPrefix) -> URL? { var temporary = URL(fileURLWithPath: NSTemporaryDirectory()) temporary.appendPathComponent(prefix) let filename = url.absoluteString.md5 + "." + url.pathExtension return temporary.appendingPathComponent(filename) } public static func savePreviewImage(url: URL, image: PlatformImage, prefix: String = Constants.previewPrefix) { var temporary = URL(fileURLWithPath: NSTemporaryDirectory()) temporary.appendPathComponent(prefix) createDirectoryIfNeeded(at: temporary) guard let cacheUrl = getCacheUrl(from: url, prefix: prefix), let data = image.jpgData else { return } try? data.write(to: cacheUrl) } private static func createDirectoryIfNeeded(at url: URL) { if !FileManager.default.fileExists(atPath: url.path) { try? FileManager.default.createDirectory( at: url, withIntermediateDirectories: true, attributes: nil ) } } public static func getImage(url: URL, size: CGSize) -> PlatformImage? { guard let image = loadImage(from: url, size: size) else { return nil } return getCachedOrResizedImage(original: image, url: url, size: size) } private static func loadImage(from url: URL, size: CGSize) -> PlatformImage? { if url.isVideo { return generateVideoThumbnail(from: url, size: size) } guard let imageData = try? Data(contentsOf: url) else { return nil } #if os(OSX) return NSImage(data: imageData) #else return UIImage(data: imageData) #endif } private static func generateVideoThumbnail(from url: URL, size: CGSize) -> PlatformImage? { let asset = AVURLAsset(url: url, options: nil) let generator = AVAssetImageGenerator(asset: asset) guard let cgImage = try? generator.copyCGImage( at: CMTimeMake(value: 0, timescale: 1), actualTime: nil ) else { return nil } #if os(OSX) return NSImage(cgImage: cgImage, size: size) #else return UIImage(cgImage: cgImage) #endif } private static func getCachedOrResizedImage( original: PlatformImage, url: URL, size: CGSize ) -> PlatformImage? { let cacheURL = getCacheUrl(from: url, prefix: Constants.thumbnailPrefix) if let cacheURL = cacheURL, FileManager.default.fileExists(atPath: cacheURL.path) { #if os(OSX) if let cached = NSImage(contentsOfFile: cacheURL.path) { return cached } #else if let cached = UIImage(contentsOfFile: cacheURL.path) { return cached } #endif } guard let resized = original.resized(to: size) else { return original } savePreviewImage(url: url, image: resized, prefix: Constants.thumbnailPrefix) return resized } private static func resizeImage(_ image: PlatformImage, to size: CGSize) -> PlatformImage? { return image } } // MARK: - macOS Specific Methods #if os(OSX) extension NoteAttachment { private func createImageMacOS( text: String, imageSize: CGSize, attributes: [NSAttributedString.Key: Any], textSize: CGSize ) -> NSImage? { let imageRect = NSRect(origin: .zero, size: imageSize) let image = NSImage(size: imageRect.size) image.lockFocus() defer { image.unlockFocus() } // Fill background (attributes[.backgroundColor] as? NSColor)?.setFill() imageRect.fill() // Draw centered text let textRect = calculateCenteredRect(textSize: textSize, containerSize: imageSize) text.draw(in: textRect, withAttributes: attributes) return image } } #endif // MARK: - iOS Specific Methods #if os(iOS) extension NoteAttachment { private func createImageIOS( text: String, imageSize: CGSize, attributes: [NSAttributedString.Key: Any], textSize: CGSize ) -> UIImage? { let imageRect = CGRect(origin: .zero, size: imageSize) UIGraphicsBeginImageContextWithOptions(imageRect.size, false, 0.0) defer { UIGraphicsEndImageContext() } guard let context = UIGraphicsGetCurrentContext() else { return nil } // Fill background (attributes[.backgroundColor] as? UIColor)?.setFill() context.fill(imageRect) // Draw centered text let textRect = calculateCenteredRect(textSize: textSize, containerSize: imageSize) text.draw(in: textRect, withAttributes: attributes) return UIGraphicsGetImageFromCurrentImageContext() } } #endif ================================================ FILE: FSNotesCore/Business/NoteContainer.swift ================================================ // // NoteContainer.swift // FSNotes // // Created by Oleksandr Glushchenko on 3/4/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // public enum NoteContainer: Int { case none = 0x01 case textBundle = 0x02 case textBundleV2 = 0x03 case encryptedTextPack = 0x04 static func withExt(rawValue: String) -> NoteContainer { switch rawValue { case "textbundle": return .textBundleV2 case "etp": return .encryptedTextPack default: return .none } } public var uti: String { switch self { case .textBundle: return "com.apple.package" case .textBundleV2: return "com.apple.package" case .encryptedTextPack: return "es.fsnot.etp.package" case .none: return "" } } public var tag: Int { switch self { case .textBundle: return 0x02 case .textBundleV2: return 0x03 case .encryptedTextPack: return 0x04 case .none: return 0x01 } } } ================================================ FILE: FSNotesCore/Business/NoteType.swift ================================================ // // NoteFileType.swift // FSNotes // // Created by Oleksandr Glushchenko on 1/6/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation public enum NoteType: String { case Markdown = "md" static func withExt(rawValue: String) -> NoteType { switch rawValue { case "markdown", "md", "mkd", "txt": return NoteType.Markdown default: return NoteType.Markdown } } static func withTag(rawValue: Int) -> NoteType { switch rawValue { case 1: return .Markdown default: return .Markdown } } static func withUTI(rawValue: String) -> NoteType { switch rawValue { case "net.daringfireball.markdown": return .Markdown default: return .Markdown } } public var tag: Int { get { switch self { case .Markdown: return 1 } } } public var uti: String { get { switch self { case .Markdown: return "net.daringfireball.markdown" } } } public func getExtension(for container: NoteContainer) -> String { return UserDefaultsManagement.noteExtension } } ================================================ FILE: FSNotesCore/Business/PreviewState.swift ================================================ // // PreviewStateswift // FSNotes // // Created by Олександр Глущенко on 20.09.2020. // Copyright © 2020 Oleksandr Glushchenko. All rights reserved. // public enum PreviewState: String { case on case off } ================================================ FILE: FSNotesCore/Business/ProgressState.swift ================================================ // // ActionState.swift // FSNotes // // Created by Олександр Глущенко on 20.09.2020. // Copyright © 2020 Oleksandr Glushchenko. All rights reserved. // public enum ProgressState: String { case none case incomplete case done } ================================================ FILE: FSNotesCore/Business/Project+Date.swift ================================================ // // Proect.swift // FSNotes // // Created by Oleksandr Hlushchenko on 08.01.2026. // Copyright © 2026 Oleksandr Hlushchenko. All rights reserved. // import Foundation extension Project { public func saveModificationTime() { let dbUrl = url.appendingPathComponent(".mtime") var content = String() guard let projects = getAllChild() else { return } for project in projects { let notes = project.getNotes() for note in notes { content += note.modifiedLocalAt.ISO8601Format() + " " + note.getRelatedPath() + "\n" } } try? FileManager.default.removeItem(at: dbUrl) try? content.write(to: dbUrl, atomically: true, encoding: .utf8) } public func saveCreationTime() { let dbUrl = url.appendingPathComponent(".ctime") var content = String() guard let projects = getAllChild() else { return } for project in projects { let notes = project.getNotes() for note in notes { if let date = note.creationDate { content += date.ISO8601Format() + " " + note.getRelatedPath() + "\n" } } } try? FileManager.default.removeItem(at: dbUrl) try? content.write(to: dbUrl, atomically: true, encoding: .utf8) } } ================================================ FILE: FSNotesCore/Business/Project.swift ================================================ // // Project.swift // FSNotes // // Created by Oleksandr Glushchenko on 4/7/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation import CoreServices public class Project: NSObject { var storage: Storage var url: URL public var label: String var isTrash: Bool var isCloudDrive: Bool = false public var parent: Project? var isDefault: Bool public var isVirtual = false public var isBookmark: Bool = false public var settings = ProjectSettings() // all notes loaded with cache diff comparsion public var isReadyForCacheSaving = false // if notes loaded from cache validation with fs needed public var isNeededCacheValidation = false public var child = [Project]() public var isExpanded = false public var isEncrypted = false public var password: String? public var settingsKey = String() public var commitsCache = [String: [String]]() public var isCleanGit = false public var gitStatus: String? public var isActiveGit = false init(storage: Storage, url: URL, label: String? = nil, isTrash: Bool = false, parent: Project? = nil, isDefault: Bool = false, isBookmark: Bool = false, isVirtual: Bool = false ) { self.storage = storage self.url = url.standardized self.isTrash = isTrash self.parent = parent self.isDefault = isDefault self.isBookmark = isBookmark self.isVirtual = isVirtual self.label = String() super.init() self.settingsKey = getSettingsKey() self.loadLabel(label) self.isCloudDrive = isCloudDriveFolder(url: url) if isDefault { #if os(iOS) settings.showInSidebar = false #endif } if let settings = getSettings() { self.settings = settings } if isTrash { settings.showInCommon = false } // Backward compatibility if settings.gitOrigin == nil, self.isDefault, let origin = UserDefaultsManagement.gitOrigin { settings.setOrigin(origin) } } public override func isEqual(_ object: Any?) -> Bool { guard let other = object as? Project else { return false } return self.url == other.url } public override var hash: Int { return url.hashValue } public func getLongSettingsKey() -> String { return "es.fsnot.project-settings\(settingsKey)" } public func saveSettings() { do { NSKeyedArchiver.setClassName("ProjectSettings", for: ProjectSettings.self) let data = try NSKeyedArchiver.archivedData(withRootObject: settings, requiringSecureCoding: true) let key = getLongSettingsKey() #if CLOUD_RELATED_BLOCK let keyStore = NSUbiquitousKeyValueStore.default keyStore.set(data, forKey: key) keyStore.synchronize() #else if let documentDir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first { let url = documentDir.appendingPathComponent(key) try? data.write(to: url) } #endif } catch { print("Settings arc error: \(error.localizedDescription)") } } public func getSettings() -> ProjectSettings? { let key = getLongSettingsKey() var data: Data? #if CLOUD_RELATED_BLOCK let keyStore = NSUbiquitousKeyValueStore.default data = keyStore.data(forKey: key) #else if let documentDir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first { let url = documentDir.appendingPathComponent(key) data = try? Data(contentsOf: url) } #endif NSKeyedUnarchiver.setClass(ProjectSettings.self, forClassName: "ProjectSettings") if let data = data, let settings = try? NSKeyedUnarchiver.unarchivedObject(ofClass: ProjectSettings.self, from: data) { return settings } return nil } public func reloadSettings() { if let settings = getSettings() { self.settings = settings loadNotesPreview() } } public func getSettingsKey() -> String { var prefix = String() // iCloud Documents if let path = getCloudDriveRelativePath() { prefix = "i\(path)" // Local documents } else if let path = getLocalDocumentsRelativePath() { prefix = "l\(path)" // External } else { prefix = "e\(url.path)" } return prefix.md5 } public static func == (lhs: Project, rhs: Project) -> Bool { return lhs.url == rhs.url } public func loadLabel(_ label: String? = nil) { if let l = label { self.label = l } else { self.label = url.lastPathComponent } var localizedName: AnyObject? try? (url as NSURL).getResourceValue(&localizedName, forKey: URLResourceKey.localizedNameKey) if let name = localizedName as? String, name.count > 0 { self.label = name } isEncrypted = getEncryptionStatus() if settings.sortBy == .none, self.label == "Welcome" { settings.sortBy = .title settings.sortDirection = .asc settings.showInCommon = false } } public func getCacheURL() -> URL? { guard let cacheDir = storage.getCacheDir() else { return nil } let key = self.url.path.md5 let cacheFile = cacheDir.appendingPathComponent(key + ".cache") return cacheFile } public func saveCache() { guard isReadyForCacheSaving, let cacheURL = getCacheURL() else { return } var notes = storage.noteList.filter({ $0.project == self }) for note in notes { if note.isEncrypted() { _ = note.lock() } } // Deduplicate let deduplicatedNotes = notes.reduce(into: [String: Note]()) { result, object in result[object.url.path] = object }.values notes = Array(deduplicatedNotes) let meta = notes.filter({ $0.isValidForCaching() }).map({ $0.getMeta() }) let jsonEncoder = JSONEncoder() do { let data = try jsonEncoder.encode(meta) try data.write(to: cacheURL) print("Cache saved for: \(self.label)") } catch { print("Serialization error.") } } public func removeCache() { guard let cacheURL = getCacheURL() else { return } do { if FileManager.default.fileExists(atPath: cacheURL.path) { try FileManager.default.removeItem(at: cacheURL) print("Cache removed successfully at: \(cacheURL.path)") } } catch { print("Cache removing error: \(error)") } } public func loadCache() -> [NoteMeta]? { guard let cacheURL = getCacheURL() else { return nil } if let data = try? Data(contentsOf: cacheURL) { let decoder = JSONDecoder() do { return try decoder.decode(Array.self, from: data) } catch { print(error) } } return nil } public func fetchNotes() -> [Note] { var notes = [Note]() let documents = fetchAllDocuments(at: url) for document in documents { let url = (document.0 as URL).standardized let modified = document.1 let created = document.2 if (url.absoluteString.isEmpty) { continue } let note = Note(url: url, with: self, modified: modified, created: created) if note.isTextBundle() && !note.isFullLoadedTextBundle() { continue } notes.append(note) } return notes } public func loadNotes(cacheOnly: Bool = false) -> [Note] { var notes = [Note]() if let metas = loadCache() { for noteMeta in metas { let note = Note(meta: noteMeta, project: self) notes.append(note) } print("From cache: \(notes.count)") isNeededCacheValidation = true } else if !cacheOnly { notes = fetchNotes() print("From disk: \(notes.count), lbl: \(label)") } loadPins(for: notes) for note in notes { storage.add(note) } loadNotesPreview() _ = loadWebAPI() return notes } public func fetchAllDocuments(at url: URL) -> [(URL, Date, Date)] { let url = url.standardized var directoryFiles = [URL]() if let enumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: nil, options: .skipsSubdirectoryDescendants) { while let file = enumerator.nextObject() as? URL { if storage.isValidNote(url: file) { directoryFiles.append(file) } } } let results = directoryFiles.map { url in ( url, (try? url.resourceValues(forKeys: [.contentModificationDateKey]) )?.contentModificationDate ?? Date.distantPast, (try? url.resourceValues(forKeys: [.creationDateKey]) )?.creationDate ?? Date.distantPast ) } return results.map { if $0.0.pathExtension == "textbundle" { return ( URL(fileURLWithPath: $0.0.path, isDirectory: false), $0.1, $0.2 ) } return $0 } } public func loadPins(for notes: [Note]) { #if CLOUD_RELATED_BLOCK for note in notes { note.isPinned = false } let store = NSUbiquitousKeyValueStore.default let names = store.array(forKey: "co.fluder.fsnotes.pins.shared") as? [String] ?? [] let pinned = Set(names) for note in notes { note.isPinned = pinned.contains(note.getRelatedPath()) } #endif } func fileExist(fileName: String, ext: String) -> Bool { let fileURL = url.appendingPathComponent(fileName + "." + ext) return FileManager.default.fileExists(atPath: fileURL.path) } func fileExistCaseInsensitive(fileName: String, ext: String) -> Bool { let fileURL = url.appendingPathComponent(fileName + "." + ext) if let note = storage.getBy(url: fileURL) { return FileManager.default.fileExists(atPath: note.url.path) } return FileManager.default.fileExists(atPath: fileURL.path) } private func isCloudDriveFolder(url: URL) -> Bool { if let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)? .appendingPathComponent("Documents", isDirectory: true) .standardized { if FileManager.default.fileExists(atPath: iCloudDocumentsURL.path, isDirectory: nil), url.path.contains(iCloudDocumentsURL.path) { return true } } return false } private func getCloudDriveRelativePath() -> String? { if let iCloudDir = FileManager.default.url(forUbiquityContainerIdentifier: nil)? .appendingPathComponent("Documents", isDirectory: true) .standardized, url.path.contains(iCloudDir.path) { return url.path.replacingOccurrences(of: iCloudDir.path, with: "") } return nil } private func getLocalDocumentsRelativePath() -> String? { if let documentDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first, url.path.contains(documentDir.path) { return url.path.replacingOccurrences(of: documentDir.path, with: "") } return nil } public func getParent() -> Project { if isDefault || isBookmark { return self } if let parent = self.parent { return parent.getParent() } return self } public func isVisibleInCommon() -> Bool { if !settings.showInCommon { return false } var parent = self.parent while parent != nil { if let unwrapped = parent?.parent { if !unwrapped.settings.showInCommon { return false } parent = unwrapped continue } return parent?.settings.showInCommon == true } return settings.showInCommon } public func getNestedLabel() -> String { var project: Project? = self var result = String() while project != nil { if let unwrappedProject = project { if result.count > 0 { result = unwrappedProject.label + " › " + result } else { result = unwrappedProject.label } project = unwrappedProject.parent } else { project = nil } } return result } public func getFullLabel() -> String { if isDefault || isBookmark { if isBookmark { return "External › " + label } return label } if isTrash { return "Trash" } return "FSNotes › \(label)" } public func getRelativePath() -> String? { if let iCloudRoot = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents").standardized { let path = url.path.replacingOccurrences(of: iCloudRoot.path, with: "") return path.md5 } return nil } public func getPathChecksum() -> String { if !UserDefaultsManagement.iCloudDrive, let documentDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first { var path = url.path.replacingOccurrences(of: documentDir.path, with: "") if path == "" { path = "Local" } return path.md5 } else { return url.path.md5 } } public func getMd5CheckSum() -> String { return url.path.md5 } public func remove() { do { try FileManager.default.removeItem(at: url) } catch { print(error) } } public func create() { do { try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil) } catch { print(error) } } public func getAllTags() -> [String] { let notes = Storage.shared().noteList.filter({ $0.project == self }) var tags: Set = [] for note in notes { for tag in note.tags { if !tags.contains(tag) { tags.insert(tag) } } } return Array(tags) } public func checkFSAndMemoryDiff() -> ([Note], [Note], [Note]) { var foundRemoved = [Note]() var foundAdded = [Note]() var foundChanged = [Note]() let memoryNotes = Storage.shared().noteList.filter({ $0.project == self }) let fileSystemNotes = fetchNotes() let cachedNotes = Set(memoryNotes.map({ $0.url })) let currentNotes = Set(fileSystemNotes.map({ $0.url })) let removed = cachedNotes.subtracting(currentNotes) let added = currentNotes.subtracting(cachedNotes) for removeURL in removed { if let note = memoryNotes.first(where: { $0.url == removeURL }) { foundRemoved.append(note) storage.noteList.removeAll(where: { $0 == note }) } } for addURL in added { if let note = fileSystemNotes.first(where: { $0.url == addURL }) { note.load() foundAdded.append(note) storage.noteList.append(note) } } for memoryNote in memoryNotes { if let note = fileSystemNotes.first(where: { $0.url == memoryNote.url }) { if memoryNote.modifiedLocalAt != note.modifiedLocalAt { memoryNote.forceLoad() foundChanged.append(memoryNote) } } } isReadyForCacheSaving = true return (foundRemoved, foundAdded, foundChanged) } public func isExpandable() -> Bool { return child.count > 0 } public func getAllChild() -> [Project]? { var projects = [Project]() projects.append(self) for item in child { if item.child.count > 0 { if let sub = item.getAllChild() { projects.append(contentsOf: sub) } } else { projects.append(item) } } return projects } public func getChildProjects() -> [Project]? { var projects = [Project]() for item in child { if item.child.count > 0 { if let sub = item.getAllChild() { projects.append(contentsOf: sub) } } else { projects.append(item) } } return projects } public func getChildProjectsByURL() -> [Project] { return storage .projects .filter({ $0.url.path.startsWith(string: url.path) && $0.url.path != url.path }) .sorted(by: { $0.url.path.components(separatedBy: "/").count < $1.url.path.components(separatedBy: "/").count }) } public func getHistoryURL() -> URL? { let url = storage.getRevisionsHistoryDocumentsSupport() return url.appendingPathComponent(getMd5CheckSum()) } public func getNotes() -> [Note] { return storage.noteList.filter({ $0.project.url.path == self.url.path }) } public func countNotes(contains image: URL) -> Int { let notes = getNotes() var qty = 0 for note in notes { if let images = note.imageUrl { if images.contains(where: { $0.path == image.path }) { qty += 1 } } } return qty } public func getEncryptionStatusFilePath() -> URL { return url.appendingPathComponent(".encrypt", isDirectory: false) } public func getEncryptionStatus() -> Bool { let encFolder = getEncryptionStatusFilePath() if FileManager.default.fileExists(atPath: encFolder.path) { return true } return false } public func isLocked() -> Bool { return password == nil && isEncrypted } public func encrypt(password: String) -> [Note] { if isEncrypted { return [Note]() } let encFolder = getEncryptionStatusFilePath() FileManager.default.createFile(atPath: encFolder.path, contents: nil) isEncrypted = true let notes = storage.getNotesBy(project: self) var encrypted = [Note]() for note in notes { if note.encrypt(password: password) { encrypted.append(note) } } self.password = nil return encrypted } public func decrypt(password: String) -> [Note] { if !isEncrypted { return [Note]() } let notes = storage.getNotesBy(project: self) var decrypted = [Note]() var qty = 0 for note in notes { if note.unEncrypt(password: password) { qty += 1 decrypted.append(note) } } guard qty > 0 || notes.count == 0 else { return [Note]() } let encFolder = getEncryptionStatusFilePath() try? FileManager.default.removeItem(at: encFolder) isEncrypted = false return decrypted } public func unlock(password: String) -> ([Note], [Note]) { let notes = self.storage.getNotesBy(project: self) var unlocked = [Note]() if notes.count == 0 { self.password = password return (notes, unlocked) } for note in notes { if note.unLock(password: password) { self.password = password unlocked.append(note) } } return (notes, unlocked) } public func lock() -> [Note] { var locked = [Note]() let notes = self.storage.getNotesBy(project: self) for note in notes { if note.lock() { locked.append(note) } } password = nil return locked } public func checkNotesCacheDiff(isGit: Bool = false) -> ([Note], [Note], [Note]) { // if not cached – load all results for cache // (not loaded instantly because is resource consumption operation, loaded later in background) guard isNeededCacheValidation || isGit else { _ = storage.noteList .filter({ $0.project == self }) .map({ $0.load() }) isReadyForCacheSaving = true return ([], [], []) } let results = checkFSAndMemoryDiff() print("Cache diff found: removed - \(results.0.count), added - \(results.1.count), modified - \(results.2.count), lbl: \(label)") return results } public func getProjectsFSAndMemoryDiff() -> ([Project], [Project]) { var foundRemoved = [Project]() var foundAdded = [Project]() var memoryProjects = [Project]() var fileSystemURLs = [URL]() if let child = getChildProjects() { memoryProjects = child } if let fsURLs = fetchAllDirectories() { fileSystemURLs = fsURLs } let cachedProjects = Set(memoryProjects.compactMap({ $0.url })) let currentProjects = Set(fileSystemURLs) let removed = cachedProjects.subtracting(currentProjects) let added = currentProjects.subtracting(cachedProjects) for removeURL in removed { if let project = memoryProjects.first(where: { $0.url == removeURL }) { foundRemoved.append(project) } } for addURL in added { let project = Project(storage: storage, url: addURL) foundAdded.append(project) } foundAdded = foundAdded.sorted(by: { $0.url.path.components(separatedBy: "/").count < $1.url.path.components(separatedBy: "/").count }) foundRemoved = foundRemoved.sorted(by: { $0.url.path.components(separatedBy: "/").count > $1.url.path.components(separatedBy: "/").count }) return (foundRemoved, foundAdded) } private func fetchAllDirectories() -> [URL]? { let maxDirs = UserDefaultsManagement.maxChildDirs guard let fileEnumerator = FileManager.default.enumerator( at: url, includingPropertiesForKeys: nil, options: FileManager.DirectoryEnumerationOptions() ) else { return nil } let extensions = ["md", "markdown", "txt", "fountain", "textbundle", "etp", "jpg", "png", "gif", "jpeg", "json", "JPG", "PNG", ".icloud", ".cache", ".Trash", "i"] let urls = fileEnumerator.allObjects.compactMap({ $0 as? URL }) .filter({ !extensions.contains($0.pathExtension) && !extensions.contains($0.lastPathComponent) && !$0.path.contains("/assets") && !$0.path.contains("/.cache") && !$0.path.contains("/files") && !$0.path.contains("/.Trash") && !$0.path.contains("/Trash") && !$0.path.contains(".textbundle") && !$0.path.contains(".revisions") && !$0.path.contains("/.") && $0 != UserDefaultsManagement.trashURL }) var fin = [URL]() var i = 0 for url in urls { do { var isDirectoryResourceValue: AnyObject? try (url as NSURL).getResourceValue(&isDirectoryResourceValue, forKey: URLResourceKey.isDirectoryKey) var isPackageResourceValue: AnyObject? try (url as NSURL).getResourceValue(&isPackageResourceValue, forKey: URLResourceKey.isPackageKey) if isDirectoryResourceValue as? Bool == true, isPackageResourceValue as? Bool == false, url.isHidden() == false { i = i + 1 fin.append(url) } } catch let error as NSError { print("Error: ", error.localizedDescription) } if i > maxDirs { break } } return fin } public func loadNotesContent() { let notes = getNotes() for note in notes { note.load() } } public func getNestedPath() -> String { var project: Project? = self var result = String() while project != nil { if project?.parent == nil { return result } if let unwrappedProject = project { if result.count > 0 { result = unwrappedProject.label + "/" + result } else { result = unwrappedProject.label } project = unwrappedProject.parent } else { project = nil } } return result } public func saveNotesPreview() { let notes = getNotes() var result = [String]() for note in notes { if note.previewState { result.append(note.name) } } settings.notesPreview = result saveSettings() } public func loadNotesPreview() { let names = settings.notesPreview let notes = storage.getNotesBy(project: self) for note in notes { note.previewState = names.contains(note.name) } } public func saveWebAPI() { let notes = getNotes() var result = [String: String]() for note in notes { if let apiId = note.apiId { result[note.name] = apiId } } settings.notesAPI = result saveSettings() } public func loadWebAPI() -> ([Note], [Note])? { guard let items = settings.notesAPI else { return nil } var keys = [String]() for (key, _) in items { keys.append(key) } let notes = storage.getNotesBy(project: self) var added = [Note]() var removed = [Note]() for note in notes { if note.apiId != nil && !keys.contains(note.name) { removed.append(note) note.apiId = nil } else if note.apiId == nil && keys.contains(note.name) { added.append(note) note.apiId = items[note.name] } } return (added, removed) } } ================================================ FILE: FSNotesCore/Business/ProjectSettings.swift ================================================ // // ProjectSettings.swift // FSNotes // // Created by Oleksandr Hlushchenko on 03.03.2023. // Copyright © 2023 Oleksandr Hlushchenko. All rights reserved. // import Foundation public class ProjectSettings: NSObject, NSSecureCoding { public static var supportsSecureCoding: Bool { return true } public var sortBy: SortBy = .none public var sortDirection: SortDirection = .desc public var showInCommon: Bool = true public var showInSidebar: Bool = true public var showNestedFoldersContent: Bool = true public var firstLineAsTitle: Bool? public var priority: Int = 0 public var gitAutoPull: Bool = false public var gitOrigin: String? public var gitPrivateKey: Data? public var gitPublicKey: Data? public var gitPrivateKeyPassphrase: String? public var notesPreview = [String]() public var notesAPI: [String: String]? public override init() {/*_*/} public required init(coder aDecoder: NSCoder) { if let value = aDecoder.decodeObject(of: NSString.self, forKey: "sortBy") as? String, let sort = SortBy(rawValue: value) { sortBy = sort } if let value = aDecoder.decodeObject(of: NSString.self, forKey: "sortDirection") as? String, let direction = SortDirection(rawValue: value) { sortDirection = direction } showInCommon = aDecoder.decodeBool(forKey: "showInCommon") showInSidebar = aDecoder.decodeBool(forKey: "showInSidebar") showNestedFoldersContent = aDecoder.decodeBool(forKey: "showNestedFoldersContent") if aDecoder.containsValue(forKey: "firstLineAsTitle") { firstLineAsTitle = aDecoder.decodeBool(forKey: "firstLineAsTitle") } priority = aDecoder.decodeInteger(forKey: "priority") gitAutoPull = aDecoder.decodeBool(forKey: "gitAutoPull") if let value = aDecoder.decodeObject(of: NSString.self, forKey: "gitOrigin") as? String { gitOrigin = value } if let value = aDecoder.decodeObject(of: NSData.self, forKey: "gitPrivateKey") as? Data { gitPrivateKey = value } if let value = aDecoder.decodeObject(of: NSData.self, forKey: "gitPublicKey") as? Data { gitPublicKey = value } if let value = aDecoder.decodeObject(of: NSString.self, forKey: "gitPrivateKeyPassphrase") as? String { gitPrivateKeyPassphrase = value } if let value = aDecoder.decodeObject(of: [NSArray.self, NSString.self], forKey: "notesPreview") as? [String] { notesPreview = value } if let value = aDecoder.decodeObject(of: [NSDictionary.self, NSString.self], forKey: "notesAPI") as? [String: String] { notesAPI = value } } public func encode(with aCoder: NSCoder) { aCoder.encode(sortBy.rawValue, forKey: "sortBy") aCoder.encode(sortDirection.rawValue, forKey: "sortDirection") aCoder.encode(showInCommon, forKey: "showInCommon") aCoder.encode(showInSidebar, forKey: "showInSidebar") aCoder.encode(showNestedFoldersContent, forKey: "showNestedFoldersContent") if let firstLineAsTitle = firstLineAsTitle { aCoder.encode(firstLineAsTitle, forKey: "firstLineAsTitle") } aCoder.encode(priority, forKey: "priority") aCoder.encode(gitAutoPull, forKey: "gitAutoPull") if let gitOrigin = gitOrigin { aCoder.encode(gitOrigin, forKey: "gitOrigin") } if let gitPrivateKey = gitPrivateKey { aCoder.encode(gitPrivateKey, forKey: "gitPrivateKey") } if let gitPublicKey = gitPublicKey { aCoder.encode(gitPublicKey, forKey: "gitPublicKey") } if let gitPrivateKeyPassphrase = gitPrivateKeyPassphrase { aCoder.encode(gitPrivateKeyPassphrase, forKey: "gitPrivateKeyPassphrase") } aCoder.encode(notesPreview, forKey: "notesPreview") if let notesAPI = self.notesAPI { aCoder.encode(notesAPI, forKey: "notesAPI") } } public func setOrigin(_ origin: String?) { if let origin = origin, origin.count > 0 { gitOrigin = origin return } gitOrigin = nil } public func isFirstLineAsTitle() -> Bool { if let firstLineAsTitle = firstLineAsTitle { return firstLineAsTitle } return UserDefaultsManagement.firstLineAsTitle } } ================================================ FILE: FSNotesCore/Business/RuntimeError.swift ================================================ // // File.swift // FSNotes // // Created by Oleksandr Glushchenko on 3/15/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // struct RuntimeError: Error { let message: String init(_ message: String) { self.message = message } public var localizedDescription: String { return message } } ================================================ FILE: FSNotesCore/Business/SearchQuery.swift ================================================ // // SearchQuery.swift // FSNotes iOS // // Created by Александр on 23.01.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // class SearchQuery { var type: SidebarItemType? = nil var projects = [Project]() var tags = [String]() var terms: [Substring]? = nil var tagsAnd: Bool = false public var filter = String() init() {} public func tagsModifierAnd(_ value: Bool = false) { tagsAnd = value } public func setType(_ type: SidebarItemType) { self.type = type } public func setFilter(_ filter: String) { if filter.hasPrefix("\"") && filter.hasSuffix("\"") { let clean = String(filter.dropFirst().dropLast()) if clean.count > 0 { self.filter = clean self.terms = [Substring(clean)] return } } self.filter = filter terms = filter.split(separator: " ") } public func isFit(note: Note) -> Bool { return !note.name.isEmpty && ( self.filter.isEmpty || self.terms != nil && self.isMatched(note: note, terms: self.terms!) ) && ( self.type == .All && note.project.isVisibleInCommon() || self.type == .Inbox && note.project.isDefault || self.type == .Trash || self.type == .Untagged && note.tags.count == 0 || self.type == .Todo && note.project.settings.showInCommon || !UserDefaultsManagement.inlineTags && self.tags.count > 0 || self.type != .Inbox && self.projects.contains(note.project) ) && ( self.type == .Trash && note.isTrash() || self.type != .Trash && !note.isTrash() ) && ( self.tags.count == 0 || UserDefaultsManagement.inlineTags && self.tags.count > 0 && ( !tagsAnd && note.tags.filter( { self.contains(tag: $0, in: self.tags) } ).count > 0 || tagsAnd && Set(self.tags).isSubset(of: Set(note.tags)) ) ) && !( note.project.isEncrypted && note.project.isLocked() ) && ( self.type != .Todo || self.type == .Todo && note.content.hasTodoAttribute() ) } public func contains(tag name: String, in tags: [String]) -> Bool { var found = false for tag in tags { if name == tag || name.starts(with: tag + "/") { found = true break } } return found } private func isMatched(note: Note, terms: [Substring]) -> Bool { for term in terms { if note.name.range(of: term, options: [.caseInsensitive, .diacriticInsensitive], range: nil, locale: nil) != nil || note.content.string.range(of: term, options: [.caseInsensitive, .diacriticInsensitive], range: nil, locale: nil) != nil { continue } return false } return true } public func dropFilter() { self.terms = nil self.filter = String() } } ================================================ FILE: FSNotesCore/Business/SettingsFilesNaming.swift ================================================ // // SettingsFilesNaming.swift // FSNotes iOS // // Created by Олександр Глущенко on 19.06.2020. // Copyright © 2020 Oleksandr Glushchenko. All rights reserved. // import Foundation enum SettingsFilesNaming: Int { case uuid case autoRename case untitledNote case date case altDate case autoRenameNew public var tag: Int { switch self { case .uuid: return 0x00 case .autoRename: return 0x01 case .untitledNote: return 0x02 case .date: return 0x03 case .altDate: return 0x04 case .autoRenameNew: return 0x05 } } public func getName() -> String { switch self { case .uuid, .autoRename, .autoRenameNew: return UUID().uuidString case .untitledNote: return "Untitled Note" case .date: let formatter = DateFormatter() formatter.dateFormat = "yyyyMMddHHmmss" return formatter.string(from: Date()) case .altDate: let dateFromatter = DateFormatter() dateFromatter.amSymbol = "AM" dateFromatter.pmSymbol = "PM" dateFromatter.dateFormat = "yyyy-MM-dd hh.mm.ss a" let date = dateFromatter.string(from: Date()) return date } } } ================================================ FILE: FSNotesCore/Business/SidebarItem.swift ================================================ // // SidebarItem.swift // FSNotes // // Created by Oleksandr Glushchenko on 4/7/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // #if os(OSX) import Cocoa #else import UIKit #endif class SidebarItem { var name: String var project: Project? var type: SidebarItemType public var icon: Image? public var tag: FSTag? init(name: String, project: Project? = nil, type: SidebarItemType, icon: Image? = nil, tag: FSTag? = nil) { self.name = name self.project = project self.type = type self.icon = icon self.tag = tag #if os(iOS) if let icon = type.icon { self.icon = getIcon(name: icon) } guard let project = project, type == .Project else { return } if project.isEncrypted { if project.isLocked() { self.type = .ProjectEncryptedLocked } else { self.type = .ProjectEncryptedUnlocked } } else { self.type = .Project } if let icon = self.type.icon { self.icon = getIcon(name: icon) } #endif } public func setType(type: SidebarItemType) { self.type = type if let icon = self.type.icon { self.icon = getIcon(name: icon) } } public func getName() -> String { return name } public func isSelectable() -> Bool { if type == .Header && project == nil { return false } if type == .Separator { return false } return true } public func isTrash() -> Bool { return (type == .Trash) } public func isGroupItem() -> Bool { let notesLabel = NSLocalizedString("Notes", comment: "Sidebar label") let trashLabel = NSLocalizedString("Trash", comment: "Sidebar label") if project == nil && [notesLabel, trashLabel].contains(name) { return true } return false } public func isSystem() -> Bool { let system: [SidebarItemType] = [.All, .Trash, .Todo, .Untagged, .Inbox] return system.contains(type) } public func load(type: SidebarItemType) { self.type = type if let icon = type.icon { self.icon = getIcon(name: icon) } } #if os(OSX) public func getIcon(name: String, white: Bool = false) -> NSImage? { let image = NSImage(named: name) image?.isTemplate = true if UserDefaults.standard.value(forKey: "AppleAccentColor") != nil { return image?.tint(color: NSColor.controlAccentColor) } else if white && !NSAppearance.current.isDark { return image?.tint(color: .white) } else { return image?.tint(color: NSColor(red: 0.08, green: 0.60, blue: 0.85, alpha: 1.00)) } } #else public func getIcon(name: String) -> UIImage? { guard let image = UIImage(named: name) else { return nil } return image.imageWithColor(color1: UIColor.mainTheme) } #endif } ================================================ FILE: FSNotesCore/Business/SidebarItemType.swift ================================================ // // SidebarItemType.swift // FSNotes // // Created by Oleksandr Glushchenko on 4/7/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // enum SidebarItemType: Int { case Label = 0x00 case All = 0x01 case Trash = 0x02 case Todo = 0x06 case Inbox = 0x07 case Tag = 0x08 case Project = 0x09 case Header = 0x10 case Untagged = 0x11 case ProjectEncryptedLocked = 12 case ProjectEncryptedUnlocked = 13 case Separator = 14 public var icon: String? { switch self { case .Label: return nil case .All: return "sidebar_notes" case .Trash: return "sidebar_trash" case .Todo: return "sidebar_todo" case .Inbox: return "sidebar_inbox" case .Tag: return "sidebar_tag" case .Project: return "sidebar_project" case .Header: return "sidebar_icloud_drive" case .Untagged: return "sidebar_untagged" case .ProjectEncryptedLocked: return "sidebar_project_encrypted_locked" case .ProjectEncryptedUnlocked: return "sidebar_project_encrypted_unlocked" case .Separator: return nil } } } ================================================ FILE: FSNotesCore/Business/SortBy.swift ================================================ // // SortBy.swift // FSNotes // // Created by Oleksandr Glushchenko on 1/29/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation public enum SortBy: String { case none case modificationDate case creationDate case title } ================================================ FILE: FSNotesCore/Business/SortDirection.swift ================================================ // // SortDirection.swift // FSNotes // // Created by Олександр Глущенко on 8/20/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Foundation public enum SortDirection: String { case asc case desc } ================================================ FILE: FSNotesCore/Business/Storage.swift ================================================ // // NotesCollection.swift // FSNotes // // Created by Oleksandr Glushchenko on 8/9/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Foundation import CoreServices #if os(OSX) import Cocoa #else import UIKit #endif class Storage { public static var instance: Storage? = nil public var noteList = [Note]() public var projects = [Project]() private var imageFolders = [URL]() public var tags = [String]() var notesDict: [String: Note] = [:] public var allowedExtensions = [ "md", "markdown", "txt", "fountain", "textbundle", "etp" // Encrypted Text Pack ] public var shouldMovePrompt = false private var trashURL = URL(string: String()) private let lastNewsDate = "2026-01-10" public var isCrashedLastTime = false private var relativeInlineImagePaths = [String]() public var plainWriter = OperationQueue.init() public var ciphertextWriter = OperationQueue.init() public var searchQuery: SearchQuery = SearchQuery() private var sortByState: SortBy = .modificationDate private var sortDirectionState: SortDirection = .asc // Virtual projects public var allNotesProject: Project? public var todoProject: Project? public var untaggedProject: Project? public var welcomeProject: Project? public var welcomeNote: Note? init() { #if CLOUD_RELATED_BLOCK // Sync pins and related stuff NSUbiquitousKeyValueStore.default.synchronize() #endif // Load root print("A. Bookmarks loading is started") let bookmarksManager = SandboxBookmark.sharedInstance() bookmarksManager.load() let storageType = UserDefaultsManagement.storageType guard let url = getRoot() else { return } removeCachesIfCrashed() #if os(OSX) if storageType == .local && UserDefaultsManagement.storageType == .iCloudDrive { shouldMovePrompt = true } #endif let name = getDefaultName(url: url) let project = Project( storage: self, url: url, label: name, isDefault: true ) insertProject(project: project) assignTrash(by: project.url) assignBookmarks() } public func loadInboxAndTrash() { // Inbox _ = getDefault()?.loadNotes() // Trash _ = getDefaultTrash()?.loadNotes() // Bookmarks for project in projects { if project.isBookmark { _ = project.loadNotes() } } // Cached if let urls = getCachedTree() { for url in urls { _ = insert(url: url, cacheOnly: true) } } loadProjectRelations() plainWriter.maxConcurrentOperationCount = 1 plainWriter.qualityOfService = .userInteractive ciphertextWriter.maxConcurrentOperationCount = 1 ciphertextWriter.qualityOfService = .userInteractive #if os(iOS) checkWelcome() let revHistory = getRevisionsHistory() let revHistoryDS = getRevisionsHistoryDocumentsSupport() if FileManager.default.directoryExists(atUrl: revHistory) { try? FileManager.default.moveItem(at: revHistory, to: revHistoryDS) } if !FileManager.default.directoryExists(atUrl: revHistoryDS) { try? FileManager.default.createDirectory(at: revHistoryDS, withIntermediateDirectories: true, attributes: nil) } #endif #if os(macOS) self.restoreUploadPaths() #endif } public func insertProject(project: Project) { if projectExist(url: project.url) { print("Project exist: \(project.label)") return } projects.append(project) } public static func shared() -> Storage { guard let storage = self.instance else { self.instance = Storage() return self.instance! } return storage } private func getDefaultName(url: URL) -> String { var name = url.lastPathComponent if let iCloudURL = getCloudDrive(), iCloudURL == url { name = "iCloud Drive" } return name } public func getRoot() -> URL? { #if targetEnvironment(simulator) || os(OSX) return UserDefaultsManagement.storageUrl #else guard UserDefaultsManagement.iCloudDrive, let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)? .appendingPathComponent("Documents") .standardized else { return getLocalDocuments() } if (!FileManager.default.fileExists(atPath: iCloudDocumentsURL.path, isDirectory: nil)) { do { try FileManager.default.createDirectory(at: iCloudDocumentsURL, withIntermediateDirectories: true, attributes: nil) return iCloudDocumentsURL.standardized } catch { print("Home directory creation: \(error)") } return nil } else { return iCloudDocumentsURL.standardized } #endif } public func getLocalDocuments() -> URL? { let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.standardized return url } // removes all caches after crash private func removeCachesIfCrashed() { if UserDefaultsManagement.crashedLastTime { removeCachedTree() if let cache = getCacheDir() { if let files = try? FileManager.default.contentsOfDirectory(atPath: cache.path) { for file in files { let url = cache.appendingPathComponent(file) try? FileManager.default.removeItem(at: url) } } } } isCrashedLastTime = UserDefaultsManagement.crashedLastTime UserDefaultsManagement.crashedLastTime = true } public func getCacheDir() -> URL? { guard let cacheDir = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first, let url = URL(string: "file://" + cacheDir) else { return nil } return url } public func makeTempEncryptionDirectory() -> URL? { let url = URL(fileURLWithPath: NSTemporaryDirectory()) .appendingPathComponent("Encryption") .appendingPathComponent(UUID().uuidString) do { try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil) return url } catch { return nil } } public func getChildProjects(project: Project) -> [Project] { return projects.filter({ $0.parent == project }).sorted(by: { $0.label.lowercased() < $1.label.lowercased() }) } public func getDefault() -> Project? { return projects.first(where: { $0.isDefault }) } public func getSidebarProjects() -> [Project] { return projects .filter({ $0.isBookmark || $0.parent?.isDefault == true }) .sorted(by: { $0.label.lowercased() < $1.label.lowercased() }) .sorted(by: { $0.settings.priority < $1.settings.priority }) } public func getDefaultTrash() -> Project? { return projects.first(where: { $0.isTrash }) } public func insert(url: URL, bookmark: Bool = false, cacheOnly: Bool = false) -> [Project]? { if projectExist(url: url) || url.lastPathComponent == "i" || url.lastPathComponent == "files" || url.lastPathComponent == "assets" || url.lastPathComponent == ".icloud" || url.path.contains(".git") || url.path.contains(".revisions") || url.path.contains(".Trash") || url.path.contains(".cache") || url.path.contains("Trash") || url.path.contains("/.") || url.path.contains(".textbundle") { return nil } let project = Project(storage: self, url: url, isBookmark: bookmark) var insert = [project] let results = project.getProjectsFSAndMemoryDiff() insert.append(contentsOf: results.1) for item in insert { if !projectExist(url: item.url) { insertProject(project: item) _ = item.loadNotes(cacheOnly: cacheOnly) } } return insert } private func assignTrash(by url: URL) { var trashURL = url.appendingPathComponent("Trash", isDirectory: true) #if os(OSX) if let trash = UserDefaultsManagement.trashURL { trashURL = trash } #endif do { try FileManager.default.contentsOfDirectory(atPath: trashURL.path) } catch { var isDir = ObjCBool(false) if !FileManager.default.fileExists(atPath: trashURL.path, isDirectory: &isDir) && !isDir.boolValue { do { try FileManager.default.createDirectory(at: trashURL, withIntermediateDirectories: false, attributes: nil) print("New trash created: \(trashURL)") } catch { print("Trash dir error: \(error)") } } } guard !projectExist(url: trashURL) else { return } let project = Project(storage: self, url: trashURL, isTrash: true) insertProject(project: project) self.trashURL = trashURL } private func getCloudDrive() -> URL? { if let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents").standardized { var isDirectory = ObjCBool(true) if FileManager.default.fileExists(atPath: iCloudDocumentsURL.path, isDirectory: &isDirectory), isDirectory.boolValue { return iCloudDocumentsURL } } return nil } func projectExist(url: URL) -> Bool { return projects.contains(where: {$0.url == url}) } public func removeBy(project: Project) { self.noteList.removeAll(where: { $0.project.url == project.url }) projects.removeAll(where: { $0.url == project.url }) } public func loadNotesContent() { for note in noteList { note.load() } } public func assignBookmarks() { let bookmarksManager = SandboxBookmark.sharedInstance() let bookmarks = bookmarksManager.getRestoredUrls() for url in bookmarks { if url.pathExtension == "css" || projectExist(url: url) || UserDefaultsManagement.gitStorage == url { continue } let project = Project(storage: self, url: url, isBookmark: true) insertProject(project: project) } } func getTrash(url: URL) -> URL? { return try? FileManager.default.url(for: .trashDirectory, in: .allDomainsMask, appropriateFor: url, create: false) } public func resetCacheAttributes() { for note in self.noteList { note.cacheHash = nil } } public func getProjects() -> [Project] { return projects } public func getProjectBy(element: Int) -> Project? { if projects.indices.contains(element) { return projects[element] } return nil } public func findAllProjectsExceptDefault() -> [Project] { return projects.filter({ !$0.isDefault }) } public func getNonSystemProjects() -> [Project] { return projects.filter({ !$0.isDefault && !$0.isTrash }) } public func getAvailableProjects() -> [Project] { return projects.filter({ !$0.isDefault && !$0.isTrash && $0.settings.showInSidebar }) } public func getProjectPaths() -> [String] { var pathList: [String] = [] let projects = getProjects() for project in projects { pathList.append(NSString(string: project.url.path).expandingTildeInPath) } return pathList } public func getProjectByNote(url: URL) -> Project? { let projectURL = url.deletingLastPathComponent() return projects.first(where: { return ( $0.url == projectURL ) }) } public func getProjectBy(url: URL) -> Project? { return projects.first(where: { return ( $0.url == url ) }) } public func sortNotes(noteList: [Note], operation: BlockOperation? = nil) -> [Note] { var noteList = noteList // Pre sort by creation and modified date, title if !searchQuery.filter.isEmpty { noteList = noteList.sorted(by: { if let operation = operation, operation.isCancelled { return false } return sortQuery(note: $0, next: $1) }) } return noteList.sorted(by: { if let operation = operation, operation.isCancelled { return false } if !searchQuery.filter.isEmpty { if ($0.title == searchQuery.filter && $1.title != searchQuery.filter) { return true } if ($0.fileName == searchQuery.filter && $1.fileName != searchQuery.filter) { return true } if ( $0.title.startsWith(string: searchQuery.filter) || $0.fileName.startsWith(string: searchQuery.filter) ) && ( !$1.title.startsWith(string: searchQuery.filter) && !$1.fileName.startsWith(string: searchQuery.filter) ) { return true } return false } return sortQuery(note: $0, next: $1) }) } private func sortQuery(note: Note, next: Note) -> Bool { if note.isPinned == next.isPinned { switch self.sortByState { case .creationDate: if let prevDate = note.creationDate, let nextDate = next.creationDate { return self.sortDirectionState == .asc && prevDate < nextDate || self.sortDirectionState == .desc && prevDate > nextDate } case .modificationDate, .none: return self.sortDirectionState == .asc && note.modifiedLocalAt < next.modifiedLocalAt || self.sortDirectionState == .desc && note.modifiedLocalAt > next.modifiedLocalAt case .title: var title = note.title var nextTitle = next.title if note.isEncryptedAndLocked() { title = note.fileName } if next.isEncryptedAndLocked() { nextTitle = next.fileName } let comparisonResult = title.localizedStandardCompare(nextTitle) return self.sortDirectionState == .asc ? comparisonResult == .orderedAscending : comparisonResult == .orderedDescending } } return note.isPinned && !next.isPinned } public func isValidNote(url: URL) -> Bool { if allowedExtensions.contains(url.pathExtension) || isValidUTI(url: url) { // disallow parent dir with dot at start – https://github.com/glushchenko/fsnotes/issues/1653 let qty = url.pathComponents.count if qty > 1 { return !url.pathComponents[qty-2].startsWith(string: ".") } return true } return false } public func isValidUTI(url: URL) -> Bool { guard url.fileSize < 100000000 else { return false } guard let typeIdentifier = (try? url.resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier else { return false } let type = typeIdentifier as CFString if type == kUTTypeFolder { return false } return UTTypeConformsTo(type, kUTTypeText) } func add(_ note: Note) { if !noteList.contains(where: { $0.name == note.name && $0.project == note.project }) { noteList.append(note) } else { print("Note already exists: \(note.name) (\(note.url))") } } public func contains(note: Note) -> Bool { if noteList.contains(where: { $0.name == note.name && $0.project == note.project }) { return true } return false } func removeBy(note: Note) { if let i = noteList.firstIndex(where: {$0 === note}) { noteList.remove(at: i) } } func getNextId() -> Int { return noteList.count } func getBy(url: URL, caseSensitive: Bool = false) -> Note? { let standardized = url.standardized if caseSensitive { return noteList.first(where: { return ( $0.url.path == standardized.path ) }) } return noteList.first(where: { return ( $0.url.path.lowercased() == standardized.path.lowercased() ) }) } func getBy(name: String) -> Note? { return noteList.first(where: { return ( $0.name == name ) }) } func getBy(title: String, exclude: Note? = nil) -> Note? { return noteList.first(where: { return ( $0.title.lowercased() == title.lowercased() && !$0.isTrash() && (exclude == nil || $0 != exclude) ) }) } func getBy(fileName: String, exclude: Note? = nil) -> Note? { return noteList.first(where: { return ( $0.fileName.lowercased() == fileName.lowercased() && !$0.isTrash() && (exclude == nil || $0 != exclude) ) }) } func getBy(titleOrName: String) -> Note? { return getBy(fileName: titleOrName) ?? getBy(title: titleOrName) } func getBy(startWith: String) -> [Note]? { return noteList.filter{ $0.title.lowercased().starts(with: startWith.lowercased()) } } func getByUrl(endsWith: String) -> Note? { for note in noteList { if note.url.path.hasSuffix(endsWith) { return note } } return nil } func getBy(contains: String) -> [Note]? { return noteList.filter{ !$0.project.isTrash && $0.title.localizedCaseInsensitiveContains(contains) } } public func getTitles(by word: String? = nil) -> [String]? { var notes = noteList if let word = word { notes = notes .filter{ $0.title.range(of: word, options: .caseInsensitive) != nil && $0.project.settings.isFirstLineAsTitle() || $0.fileName.range(of: word, options: .caseInsensitive) != nil && !$0.project.settings.isFirstLineAsTitle() } .filter({ !$0.isTrash() }) guard notes.count > 0 else { return nil } var titles = notes.map{ String($0.project.settings.isFirstLineAsTitle() ? $0.title : $0.fileName) } titles = Array(Set(titles)) titles = titles .filter({ !$0.starts(with: "![](") && !$0.starts(with: "[[") }) .sorted { (first, second) -> Bool in let firstStarts = first.range(of: word, options: [.caseInsensitive, .anchored]) != nil let secondStarts = second.range(of: word, options: [.caseInsensitive, .anchored]) != nil if firstStarts && secondStarts || !firstStarts && !secondStarts { return first.localizedCaseInsensitiveCompare(second) == .orderedAscending } return firstStarts && !secondStarts } if titles.count > 100 { return Array(titles[0..<100]) } return titles } guard notes.count > 0 else { return nil } notes = notes.sorted { first, second in return first.modifiedLocalAt > second.modifiedLocalAt } let titles = notes .filter({ !$0.isTrash() }) .map{ String($0.project.settings.isFirstLineAsTitle() ? $0.title : $0.fileName ) } .filter({ $0.count > 0 }) .filter({ !$0.starts(with: "![](") }) .prefix(100) return Array(titles) } func getDemoSubdirURL() -> URL? { #if os(OSX) if let project = projects.first { return project.url } return nil #else if let icloud = UserDefaultsManagement.iCloudDocumentsContainer { return icloud } return UserDefaultsManagement.storageUrl #endif } func removeNotes(notes: [Note], fsRemove: Bool = true, completely: Bool = false, completion: @escaping ([URL: URL]?) -> ()) { #if !SHARE_EXT guard notes.count > 0 else { completion(nil) return } for note in notes { note.removeCacheForPreviewImages() removeBy(note: note) } var removed = [URL: URL]() if fsRemove { for note in notes { if let trashURLs = note.removeFile(completely: completely) { removed[trashURLs[0]] = trashURLs[1] } } } if removed.count > 0 { completion(removed) } else { completion(nil) } #endif } private func fetchAllDirectories(url: URL) -> [URL]? { let maxDirs = UserDefaultsManagement.maxChildDirs guard let fileEnumerator = FileManager.default.enumerator( at: url, includingPropertiesForKeys: nil, options: FileManager.DirectoryEnumerationOptions() ) else { return nil } var extensions = self.allowedExtensions extensions.append(contentsOf: [ "jpg", "png", "gif", "jpeg", "json", "JPG", "PNG", ".icloud", ".cache", ".Trash", "i" ]) let urls = fileEnumerator.allObjects.compactMap({ $0 as? URL }) .filter({ !extensions.contains($0.pathExtension) && !extensions.contains($0.lastPathComponent) && !$0.path.contains("/assets") && !$0.path.contains("/.cache") && !$0.path.contains("/files") && !$0.path.contains("/.Trash") && !$0.path.contains("/Trash") && !$0.path.contains(".textbundle") && !$0.path.contains(".revisions") && !$0.path.contains("/.git") }) var fin = [URL]() var i = 0 for url in urls { do { var isDirectoryResourceValue: AnyObject? try (url as NSURL).getResourceValue(&isDirectoryResourceValue, forKey: URLResourceKey.isDirectoryKey) var isPackageResourceValue: AnyObject? try (url as NSURL).getResourceValue(&isPackageResourceValue, forKey: URLResourceKey.isPackageKey) if isDirectoryResourceValue as? Bool == true, isPackageResourceValue as? Bool == false { i = i + 1 fin.append(url) } } catch let error as NSError { print("Error: ", error.localizedDescription) } if i > maxDirs { break } } return fin } public func getCurrentProject() -> Project? { return projects.first } public func getAllTrash() -> [Note] { return noteList.filter { $0.isTrash() } } private func cleanTrash() { if #available(iOS 11.0, *) { guard let trash = try? FileManager.default.url(for: .trashDirectory, in: .allDomainsMask, appropriateFor: UserDefaultsManagement.storageUrl, create: false) else { return } do { let fileURLs = try FileManager.default.contentsOfDirectory(at: trash, includingPropertiesForKeys: nil, options: []) for fileURL in fileURLs { try FileManager.default.removeItem(at: fileURL) } } catch { print(error) } } } public func saveCloudPins() { #if CLOUD_RELATED_BLOCK if let pinned = getPinned() { var names = [String]() for note in pinned { names.append(note.getRelatedPath()) } let keyStore = NSUbiquitousKeyValueStore.default keyStore.set(names, forKey: "co.fluder.fsnotes.pins.shared") keyStore.synchronize() print("Pins successfully saved: \(names)") } #endif } public func loadPins(notes: [Note]) { #if CLOUD_RELATED_BLOCK let keyStore = NSUbiquitousKeyValueStore.default keyStore.synchronize() guard let names = keyStore.array(forKey: "co.fluder.fsnotes.pins.shared") as? [String] else { return } for note in notes { if names.contains(note.getRelatedPath()) { note.addPin(cloudSave: false) } } #endif } public func restoreCloudPins() -> (removed: [Note]?, added: [Note]?) { var added = [Note]() var removed = [Note]() #if CLOUD_RELATED_BLOCK let keyStore = NSUbiquitousKeyValueStore.default keyStore.synchronize() if let names = keyStore.array(forKey: "co.fluder.fsnotes.pins.shared") as? [String] { if let pinned = getPinned() { for note in pinned { if !names.contains(note.getRelatedPath()) { note.removePin(cloudSave: false) removed.append(note) } } } for note in noteList { if !note.isPinned, names.contains(note.getRelatedPath()) { note.addPin(cloudSave: false) added.append(note) } } } #endif return (removed, added) } public func getPinned() -> [Note]? { return noteList.filter({ $0.isPinned }) } public func remove(project: Project) { if let index = projects.firstIndex(of: project) { projects.remove(at: index) cleanCachedTree(url: project.url) } removeBy(project: project) } public func getNotesBy(project: Project) -> [Note] { return noteList.filter({ $0.project == project }) } public func loadProjects(from urls: [URL]) { var result = [URL]() for url in urls { do { _ = try FileManager.default.contentsOfDirectory(atPath: url.path) result.append(url) } catch { print(error) } } let projects = result.compactMap({ Project(storage: self, url: $0)}) guard projects.count > 0 else { return } self.projects.removeAll() for project in projects { if project == projects.first { project.isDefault = true project.label = NSLocalizedString("Inbox", comment: "") } insertProject(project: project) } } public func trashItem(url: URL) -> URL? { guard let trashURL = Storage.shared().getDefaultTrash()?.url else { return nil } let fileName = url.deletingPathExtension().lastPathComponent let fileExtension = url.pathExtension var destination = trashURL.appendingPathComponent(url.lastPathComponent) var i = 0 while FileManager.default.fileExists(atPath: destination.path) { let nextName = "\(fileName)_\(i).\(fileExtension)" destination = trashURL.appendingPathComponent(nextName) i += 1 } return destination } public func getCache(key: String) -> Data? { guard let cacheDir = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first else { return nil } guard let url = URL(string: "file://" + cacheDir) else { return nil } let cacheURL = url.appendingPathComponent(key + ".cache") return try? Data(contentsOf: cacheURL) } public func saveProjectsCache() { for project in projects { project.saveCache() } saveCachedTree() } public func checkWelcome() { #if os(OSX) guard let storageUrl = getDefault()?.url else { return } guard UserDefaultsManagement.showWelcome else { return } guard let bundlePath = Bundle.main.path(forResource: "Welcome", ofType: ".bundle") else { return } let bundle = URL(fileURLWithPath: bundlePath) let url = storageUrl.appendingPathComponent("Welcome", isDirectory: true) if FileManager.default.fileExists(atPath: url.path) { return } try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil) do { var files = try FileManager.default.contentsOfDirectory(atPath: bundle.path) files = files.sorted(by: { $0.localizedStandardCompare($1) == .orderedDescending }) var i = 0 for file in files { i += 1 let dstPath = "\(url.path)/\(file)" try? FileManager.default.copyItem(atPath: "\(bundle.path)/\(file)", toPath: dstPath) // Adds sorting for global sort by .creationDate let mdPath = "\(url.path)/\(file)/text.markdown" if let attributes = try? FileManager.default.attributesOfItem(atPath: mdPath), let creationDate = attributes[.creationDate] as? Date { let newDate = creationDate.addingTimeInterval(TimeInterval(i)) try? FileManager.default.setAttributes([.creationDate: newDate], ofItemAtPath: mdPath) } } } catch { print("Initial copy error: \(error)") } let project = Project(storage: self, url: url, label: "Welcome") insertProject(project: project) let notes = project.loadNotes() _ = notes.compactMap({ $0.load() }) welcomeProject = project welcomeNote = notes.first(where: { $0.fileName == "1. Introduction"}) #else guard UserDefaultsManagement.showWelcome else { return } guard noteList.isEmpty else { return } let welcomeFileName = "Meet FSNotes 7.textbundle" guard let src = Bundle.main.resourceURL?.appendingPathComponent(welcomeFileName) else { return } guard let dst = getDefault()?.url.appendingPathComponent(welcomeFileName) else { return } do { if !FileManager.default.fileExists(atPath: dst.path) { try FileManager.default.copyItem(atPath: src.path, toPath: dst.path) if let project = getDefault() { let note = Note(url: dst, with: project) add(note) } } } catch { print("Initial copy error: \(error)") } UserDefaultsManagement.showWelcome = false #endif } public func getNewsDate() -> Date? { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" if let date = dateFormatter.date(from: lastNewsDate) { return date } return nil } public func isReadedNewsOutdated() -> Bool { guard let date = UserDefaultsManagement.lastNews, let newsDate = getNewsDate() else { return true } if newsDate > date { return true } return false } public func getNews() -> URL? { return Bundle.main.resourceURL?.appendingPathComponent("Meet FSNotes 7.textbundle") } public func loadNonSystemProject() { guard let main = getDefault() else { return } let projectURLs = getAllSubUrls(for: main.url) for projectURL in projectURLs { let project = Project(storage: self, url: projectURL) insertProject(project: project) } let bookmarkURLs = fetchBookmarkUrls() for url in bookmarkURLs { if !projectURLs.contains(url) { let project = Project(storage: self, url: url) insertProject(project: project) } } } public func fetchBookmarkUrls() -> [URL] { guard let main = getDefault()?.url else { return [URL]() } var projectURLs = [URL]() let bookmarkUrls = SandboxBookmark.sharedInstance().getRestoredUrls() for url in bookmarkUrls { if !projectURLs.contains(url) && url != main && url != self.trashURL { projectURLs.append(url) if let subUrls = fetchAllDirectories(url: url) { for sUrl in subUrls { if !projectURLs.contains(sUrl) { projectURLs.append(sUrl) } } } } } return projectURLs } private func getAllSubUrls(for rootUrl: URL) -> [URL] { let trash = trashURL var projectURLs = [URL]() if let urls = fetchAllDirectories(url: rootUrl) { for url in urls { let standardizedURL = (url as URL).standardized if standardizedURL == trash || standardizedURL == rootUrl { continue } projectURLs.append(standardizedURL) } } return projectURLs } public func getProjectDiffs() -> ([Project], [Project], [Note], [Note]) { var insert = [Project]() var remove = [Project]() if let defaultProject = getDefault() { let defaultResults = defaultProject.getProjectsFSAndMemoryDiff() remove.append(contentsOf: defaultResults.0) insert.append(contentsOf: defaultResults.1) } let externalProjects = projects.filter({ $0.isBookmark }) for project in externalProjects { let results = project.getProjectsFSAndMemoryDiff() remove.append(contentsOf: results.0) insert.append(contentsOf: results.1) } for insertItem in insert { insertProject(project: insertItem) } loadProjectRelations() saveCachedTree() var insertNotes = [Note]() for insertItem in insert { let append = insertItem.loadNotes() insertNotes.append(contentsOf: append) } var removeNotes = [Note]() for removeItem in remove { let append = getNotesBy(project: removeItem) removeNotes.append(contentsOf: append) } return (remove, insert, removeNotes, insertNotes) } public func importNote(url: URL) -> Note? { if !FileManager.default.fileExists(atPath: url.path) { return nil } guard getBy(url: url) == nil, let project = self.getProjectByNote(url: url) else { return nil } let note = Note(url: url, with: project) if note.isTextBundle() && !note.isFullLoadedTextBundle() { return nil } note.load() note.loadModifiedLocalAt() note.loadCreationDate() loadPins(notes: [note]) add(note) print("FSWatcher import note: \"\(note.name)\"") return note } public func hideImages(directory: String, srcPath: String) { if !relativeInlineImagePaths.contains(directory) { let url = URL(fileURLWithPath: directory, isDirectory: true) relativeInlineImagePaths.append(directory) if !url.isHidden(), FileManager.default.directoryExists(atUrl: url), srcPath.contains("/"), !srcPath.contains("..") { if let contentList = try? FileManager.default.contentsOfDirectory(atPath: url.path), containsTextFiles(contentList) { return } if let data = "true".data(using: .utf8) { try? url.setExtendedAttribute(data: data, forName: "es.fsnot.hidden.dir") } } } } private func containsTextFiles(_ list: [String]) -> Bool { for item in list { let ext = (item as NSString).pathExtension.lowercased() if allowedExtensions.contains(ext) { return true } } return false } public func findParent(url: URL) -> Project? { let parentURL = url.deletingLastPathComponent() if let foundParent = projects.first(where: { $0.url == parentURL}) { return foundParent } return nil } #if os(OSX) public func saveProjectsExpandState() { var urls = [URL]() for project in projects { if project.isExpanded { urls.append(project.url) } } if var documentDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first { documentDir.appendPathComponent("projects.state") if let data = try? NSKeyedArchiver.archivedData(withRootObject: urls, requiringSecureCoding: true) { try? data.write(to: documentDir) } } } public func restoreProjectsExpandState() { guard var documentDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return } documentDir.appendPathComponent("projects.state") guard let data = FileManager.default.contents(atPath: documentDir.path) else { return } guard let urls = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, NSURL.self], from: data) as? [URL] else { return } for project in projects { if urls.contains(project.url) { project.isExpanded = true } } } #endif public func getRevisionsHistory() -> URL { let documentDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first ?? URL(fileURLWithPath: NSTemporaryDirectory()) let revisionsUrl = documentDir.appendingPathComponent(".revisions") return revisionsUrl } public func getRevisionsHistoryDocumentsSupport() -> URL { let documentDir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first ?? URL(fileURLWithPath: NSTemporaryDirectory()) let revisionsUrl = documentDir.appendingPathComponent(".revisions") return revisionsUrl } public func saveUploadPaths() { let notes = noteList.filter({ $0.uploadPath != nil }) var bookmarks = [URL: String]() for note in notes { if let path = note.uploadPath, path.count > 1 { bookmarks[note.url] = path } } if let data = try? NSKeyedArchiver.archivedData(withRootObject: bookmarks, requiringSecureCoding: true) { UserDefaultsManagement.sftpUploadBookmarksData = data } } public func restoreUploadPaths() { guard let data = UserDefaultsManagement.sftpUploadBookmarksData, let uploadBookmarks = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSDictionary.self, NSURL.self, NSString.self], from: data) as? [URL: String] else { return } for bookmark in uploadBookmarks { if let note = getBy(url: bookmark.key) { note.uploadPath = bookmark.value } } } public func getGitKeysDir() -> URL? { guard let url = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask) .first? .appendingPathComponent("Keys", isDirectory: true) else { return nil } if !FileManager.default.fileExists(atPath: url.path) { try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true) } return url } public func getProjectBy(settingsKey: String) -> Project? { return projects.first(where: { return ( $0.settingsKey == settingsKey ) }) } public func hasOrigins() -> Bool { return projects.first(where: { return ( $0.settings.gitOrigin != nil && $0.settings.gitOrigin!.count > 0 ) }) != nil } public func getGitProjects() -> [Project]? { return projects.filter({ return ( $0.settings.gitOrigin != nil && $0.settings.gitOrigin!.count > 0 ) }) } public func loadProjectRelations() { for project in projects { if let parent = getProjectBy(url: project.url.deletingLastPathComponent()) { if project.isTrash { continue } project.parent = parent if parent.child.filter({ $0.url == project.url }).count == 0 { parent.child.append(project) } parent.child = parent.child.sorted(by: { $0.settings.priority < $1.settings.priority }) } } } public func saveCachedTree() { guard let cacheDir = getCacheDir() else { return } var urls = getNonSystemProjects() .sorted(by: { $0.url.path.components(separatedBy: "/").count < $1.url.path.components(separatedBy: "/").count }) .compactMap({ $0.url }) // Deduplicate let deduplicatedUrls = urls.reduce(into: [String: URL]()) { result, object in result[object.path] = object }.values urls = Array(deduplicatedUrls) if let data = try? NSKeyedArchiver.archivedData(withRootObject: urls, requiringSecureCoding: true) { let url = cacheDir.appendingPathComponent("sidebarTree") do { try data.write(to: url) print("B. Sidebar tree caching is finished") } catch { print("Sidebar caching error") } } } public func getCachedTree() -> [URL]? { guard let cacheDir = getCacheDir() else { return nil } let url = cacheDir.appendingPathComponent("sidebarTree") if let data = try? Data(contentsOf: url), let urls = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, NSURL.self], from: data) as? [URL] { return urls } return nil } public func removeCachedTree() { guard let cacheDir = getCacheDir() else { return } let url = cacheDir.appendingPathComponent("sidebarTree") try? FileManager.default.removeItem(at: url) } public func cleanCachedTree(url: URL) { guard let urls = getCachedTree() else { return } let cleanList = urls.filter({ !$0.path.startsWith(string: url.path) }) if let data = try? NSKeyedArchiver.archivedData(withRootObject: cleanList, requiringSecureCoding: false) { if let cacheDir = getCacheDir() { let url = cacheDir.appendingPathComponent("sidebarTree") do { try data.write(to: url) } catch { print("Sidebar caching error") } } } } public func getSortedProjects() -> [Project] { return self.projects.sorted(by: {$0.url.path < $1.url.path}) } public func setSearchQuery(value: SearchQuery) { self.searchQuery = value buildSortBy() } public func getSortByState() -> SortBy { return self.sortByState } public func getSortDirectionState() -> SortDirection { return self.sortDirectionState } public func buildSortBy() { if let project = self.searchQuery.projects.first, project.settings.sortBy != .none { self.sortByState = project.settings.sortBy self.sortDirectionState = project.settings.sortDirection return } if self.searchQuery.projects.count == 0 { var project: Project? switch self.searchQuery.type { case .All: project = self.allNotesProject case .Untagged: project = self.untaggedProject case .Todo: project = self.todoProject default: project = self.allNotesProject } if let project = project, project.settings.sortBy != .none { self.sortByState = project.settings.sortBy self.sortDirectionState = project.settings.sortDirection return } } self.sortByState = UserDefaultsManagement.sort self.sortDirectionState = UserDefaultsManagement.sortDirection ? .desc : .asc } public func migrationAPIIds() { guard let key = UserDefaultsManagement.deprecatedUploadKey else { return } UserDefaultsManagement.uploadKey = key UserDefaultsManagement.deprecatedUploadKey = nil guard let data = UserDefaultsManagement.apiBookmarksData, let uploadBookmarks = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSDictionary.self, NSURL.self, NSString.self], from: data) as? [URL: String] else { return } for bookmark in uploadBookmarks { if let note = getBy(url: bookmark.key) { if note.apiId == nil { note.apiId = bookmark.value note.project.saveWebAPI() } } } UserDefaultsManagement.apiBookmarksData = nil } public func addNote(url: URL) -> Note { let projectURL = url.deletingLastPathComponent() var project: Project? if let unwrappedProject = getProjectBy(url: projectURL) { project = unwrappedProject } else { project = Project(storage: self, url: projectURL) insertProject(project: project!) } let note = Note(url: url, with: project!) add(note) return note } } extension String: Error {} ================================================ FILE: FSNotesCore/Business/StorageType.swift ================================================ // // StorageType.swift // FSNotes // // Created by Олександр Глущенко on 06.05.2020. // Copyright © 2020 Oleksandr Glushchenko. All rights reserved. // import Foundation public enum StorageType: Int { case none = 0x00 case local = 0x01 case iCloudDrive = 0x02 case custom = 0x03 } ================================================ FILE: FSNotesCore/Business/TextBundleInfo.swift ================================================ // // TextBundleInfo.swift // FSNotes // // Created by Oleksandr Glushchenko on 3/4/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Foundation struct TextBundleInfo: Decodable { let version: Int let type: String let flatExtension: String? let created: Int? let modified: Int? } ================================================ FILE: FSNotesCore/Business/UndoData.swift ================================================ // // UndoData.swift // FSNotes // // Created by Oleksandr Glushchenko on 4/27/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation class UndoData: NSObject { let string: NSAttributedString let range: NSRange init(string: NSAttributedString, range: NSRange) { self.string = string self.range = range } } ================================================ FILE: FSNotesCore/CodeBlockDetector.swift ================================================ // // CodeBlockDetector.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.10.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import Foundation #if os(OSX) import Cocoa #endif class CodeBlockDetector { static let shared = CodeBlockDetector() private let pattern: String private let regex: NSRegularExpression private var previousRanges: [NSRange] = [] init() { self.pattern = "(?<=\\n|\\A)```[a-zA-Z0-9]*\\n([\\s\\S]*?)\\n```(?=\\n|\\Z)" do { self.regex = try NSRegularExpression(pattern: pattern, options: []) } catch { fatalError("Invalid regex pattern: \(error)") } } public func findCodeBlocks(in textStorage: NSAttributedString, range searchRange: NSRange? = nil) -> [NSRange] { guard UserDefaultsManagement.codeBlockHighlight else { return [] } let rangeToSearch = searchRange ?? NSRange(location: 0, length: textStorage.length) return regex.matches(in: textStorage.string, options: [], range: rangeToSearch) .map { $0.range } } public func codeBlocks(textStorage: NSMutableAttributedString, editedRange: NSRange, delta: Int, newRanges: [NSRange]?) -> CodeBlockRanges { let newRanges = newRanges ?? [] let adjustedPreviousRanges = adjustPreviousRanges(for: editedRange, delta: delta) previousRanges = newRanges let completelyNewBlocks = findCompletelyNewBlocks( new: newRanges, adjusted: adjustedPreviousRanges ) let blocksFromSplit = findBlocksFromSplit( new: newRanges, adjusted: adjustedPreviousRanges ) let blocksFromMerge = findBlocksFromMerge( new: newRanges, adjusted: adjustedPreviousRanges ) let expandedBlocks = findExpandedBlocks( new: newRanges, adjusted: adjustedPreviousRanges, excluding: completelyNewBlocks + blocksFromSplit + blocksFromMerge ) let (editedBlock, editedParagraph) = findEditedBlock( in: newRanges, editedRange: editedRange, textStorage: textStorage, excluding: completelyNewBlocks + blocksFromSplit + blocksFromMerge ) let addedBlocks = completelyNewBlocks + blocksFromSplit + blocksFromMerge + expandedBlocks let markdownRanges = findRangesBecameMarkdown( adjusted: adjustedPreviousRanges, new: newRanges ) return CodeBlockRanges( code: addedBlocks, md: markdownRanges, edited: editedBlock, editedParagraph: editedParagraph ) } // MARK: - Helper Methods private func adjustPreviousRanges(for editedRange: NSRange, delta: Int) -> [NSRange] { return previousRanges.compactMap { range -> NSRange? in let adjustedRange: NSRange if range.location >= editedRange.location { adjustedRange = NSRange(location: range.location + delta, length: range.length) } else if NSMaxRange(range) <= editedRange.location { adjustedRange = range } else { let newLength = range.length + delta adjustedRange = NSRange(location: range.location, length: max(0, newLength)) } return adjustedRange.length > 0 ? adjustedRange : nil } } private func findCompletelyNewBlocks( new: [NSRange], adjusted: [NSRange] ) -> [NSRange] { return new.filter { newRange in !adjusted.contains { oldRange in NSIntersectionRange(oldRange, newRange).length > 0 } } } private func findBlocksFromSplit( new: [NSRange], adjusted: [NSRange] ) -> [NSRange] { var result: [NSRange] = [] for oldRange in adjusted { let overlappingBlocks = new.filter { newRange in NSIntersectionRange(oldRange, newRange).length > 0 } if overlappingBlocks.count >= 2 { result.append(contentsOf: overlappingBlocks) } } return result } private func findBlocksFromMerge( new: [NSRange], adjusted: [NSRange] ) -> [NSRange] { return new.filter { newRange in let overlappingOldBlocks = adjusted.filter { oldRange in NSIntersectionRange(oldRange, newRange).length > 0 } return overlappingOldBlocks.count >= 2 } } private func findExpandedBlocks( new: [NSRange], adjusted: [NSRange], excluding: [NSRange] ) -> [NSRange] { return new.compactMap { newRange -> NSRange? in guard !excluding.contains(where: { NSEqualRanges($0, newRange) }) else { return nil } let containedOldBlocks = adjusted.filter { oldRange in NSLocationInRange(oldRange.location, newRange) && NSLocationInRange(NSMaxRange(oldRange) - 1, newRange) && !NSEqualRanges(oldRange, newRange) } guard containedOldBlocks.count == 1, let oldBlock = containedOldBlocks.first, newRange.length > oldBlock.length else { return nil } return newRange } } private func findEditedBlock( in ranges: [NSRange], editedRange: NSRange, textStorage: NSMutableAttributedString, excluding: [NSRange] ) -> (block: NSRange?, paragraph: NSRange?) { for range in ranges { guard !excluding.contains(where: { NSEqualRanges($0, range) }) else { continue } // Проверяем, что editedRange находится внутри блока let isInside = editedRange.length == 0 ? NSLocationInRange(editedRange.location, range) || editedRange.location == NSMaxRange(range) : NSIntersectionRange(range, editedRange).length > 0 guard isInside else { continue } let paragraphRange = (textStorage.string as NSString).paragraphRange(for: editedRange) let editedParagraph = NSIntersectionRange(paragraphRange, range) return (block: range, paragraph: editedParagraph) } return (block: nil, paragraph: nil) } private func findRangesBecameMarkdown( adjusted: [NSRange], new: [NSRange] ) -> [NSRange] { var result: [NSRange] = [] for oldRange in adjusted { var uncoveredRanges: [NSRange] = [oldRange] for newRange in new { uncoveredRanges = uncoveredRanges.flatMap { uncovered -> [NSRange] in let intersection = NSIntersectionRange(uncovered, newRange) guard intersection.length > 0 else { return [uncovered] } var fragments: [NSRange] = [] // Левый фрагмент if intersection.location > uncovered.location { fragments.append(NSRange( location: uncovered.location, length: intersection.location - uncovered.location )) } // Правый фрагмент if NSMaxRange(intersection) < NSMaxRange(uncovered) { fragments.append(NSRange( location: NSMaxRange(intersection), length: NSMaxRange(uncovered) - NSMaxRange(intersection) )) } return fragments } } result.append(contentsOf: uncoveredRanges) } return result } } struct CodeBlockRanges { var new: [NSRange]? var code: [NSRange]? var md: [NSRange]? var edited: NSRange? var editedParagraph: NSRange? } ================================================ FILE: FSNotesCore/Core macOS/zh-Hans-CN.lproj/InfoPlist.strings ================================================ /* Bundle name */ "CFBundleName" = "FSNotesCore_macOS"; /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2018 Oleksandr Glushchenko. Alle Rechte vorbehalten"; ================================================ FILE: FSNotesCore/Extensions/Data+.swift ================================================ // // Data+.swift // FSNotes // // Created by Александр on 03.04.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // import Foundation extension Data { var isPDF: Bool { guard self.count >= 1024 else { return false } let pdfHeader = Data(bytes: "%PDF", count: 4) return self.range(of: pdfHeader, options: [], in: Range(NSRange(location: 0, length: 1024))) != nil } mutating func append(_ string: String, using encoding: String.Encoding = .utf8) { if let data = string.data(using: encoding) { append(data) } } func getFileType() -> ImageFormat { switch self[0] { case 0x89: return .png case 0xFF: return .jpg case 0x47: return .gif case 0x49, 0x4D: return .tiff case 0x52 where self.count >= 12: let subdata = self[0...11] if let dataString = String(data: subdata, encoding: .ascii), dataString.hasPrefix("RIFF"), dataString.hasSuffix("WEBP") { return .webp } case 0x00 where self.count >= 12 : let subdata = self[8...11] if let dataString = String(data: subdata, encoding: .ascii), Set(["heic", "heix", "hevc", "hevx"]).contains(dataString) ///OLD: "ftypheic", "ftypheix", "ftyphevc", "ftyphevx" { return .heic } default: break } return .unknown } } ================================================ FILE: FSNotesCore/Extensions/Date+.swift ================================================ // // Date+.swift // FSNotes iOS // // Created by Oleksandr Glushchenko on 9/25/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation extension Date { func toMillis() -> Int64! { return Int64(self.timeIntervalSince1970 * 1000) } static func getCurrentFormattedDate() -> String { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd HH.mm.ss.SSS" return dateFormatter.string(from: Date()) } func string(format: String) -> String { let formatter = DateFormatter() formatter.dateFormat = format return formatter.string(from: self) } func removeNanoseconds() -> Date? { let calendar = Calendar.current let components: Set = [.year, .month, .day, .hour, .minute, .second] return calendar.date(from: calendar.dateComponents(components, from: self)) } func isGreaterThan(_ date: Date) -> Bool { guard let selfWithoutNanoseconds = self.removeNanoseconds(), let dateWithoutNanoseconds = date.removeNanoseconds() else { return false } return selfWithoutNanoseconds > dateWithoutNanoseconds } } ================================================ FILE: FSNotesCore/Extensions/DateFormatter+.swift ================================================ // // DateFormatter+.swift // FSNotes // // Created by Jeff Hanbury on 25/03/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation public extension DateFormatter { func formatDateForDisplay(_ date: Date) -> String { dateStyle = .short timeStyle = .none locale = NSLocale.autoupdatingCurrent return string(from: date) } func formatTimeForDisplay(_ date: Date) -> String { dateStyle = .none timeStyle = .short locale = NSLocale.autoupdatingCurrent return string(from: date) } func formatForDuplicate(_ date: Date) -> String { dateFormat = "yyyyMMddhhmmss" return string(from: date) } } ================================================ FILE: FSNotesCore/Extensions/FileManager+.swift ================================================ // // FileManager+.swift // FSNotes // // Created by Олександр Глущенко on 07.02.2021. // Copyright © 2021 Oleksandr Glushchenko. All rights reserved. // import Foundation extension FileManager { func directoryExists(atUrl url: URL) -> Bool { var isDirectory: ObjCBool = false let exists = self.fileExists(atPath: url.path, isDirectory: &isDirectory) return exists && isDirectory.boolValue } } ================================================ FILE: FSNotesCore/Extensions/NSAttributedString+.swift ================================================ // // NSAttributedString+.swift // FSNotes // // Created by Олександр Глущенко on 03.05.2020. // Copyright © 2020 Oleksandr Glushchenko. All rights reserved. // import Foundation extension NSAttributedString { public func hasTodoAttribute() -> Bool { var found = false enumerateAttribute(.todo, in: NSRange(0.. NSMutableAttributedString { let result = NSMutableAttributedString(attributedString: self) let fullRange = NSRange(location: 0, length: result.length) enumerateAttribute(.attachment, in: fullRange, options: .reverse) { _, range, _ in guard let meta = getMeta(at: range.location) else { return } let path = meta.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? meta.path let title = meta.title.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? meta.title let replacement = "![\(title)](\(path))" result.removeAttribute(.attachment, range: range) result.replaceCharacters(in: range, with: replacement) } return result } public func loadImagesAndFiles(note: Note) { let fullRange = NSRange(location: 0, length: length) var offset = 0 var images: [URL] = [] var attachments: [URL] = [] FSParser.imageInlineRegex.matches(string, range: fullRange) { result in guard let result = result else { return } var range = result.range range.location -= offset let title = result.optionalRange(at: 2).flatMap { self.mutableString.substring(with: NSRange(location: $0.location - offset, length: $0.length)) } ?? "" let path = result.optionalRange(at: 3).flatMap { self.mutableString.substring(with: NSRange(location: $0.location - offset, length: $0.length)) } ?? "" guard let cleanPath = path.removingPercentEncoding, let fileURL = note.getAttachmentFileUrl(name: cleanPath) else { return } if fileURL.isRemote() { return } else if FileManager.default.fileExists(atPath: fileURL.path), fileURL.isImage || fileURL.isVideo { images.append(fileURL) } else { attachments.append(fileURL) } let attributedAttachment = NSMutableAttributedString(url: fileURL, title: title, path: cleanPath) self.replaceCharacters(in: range, with: attributedAttachment) offset += range.length - 1 } note.imageUrl = images note.attachments = attachments } public func unloadTasks() -> NSMutableAttributedString { let result = NSMutableAttributedString(attributedString: self) var offset = 0 let fullRange = NSRange(location: 0, length: length) enumerateAttribute(.attachment, in: fullRange) { value, range, _ in guard value != nil, range.length == 1, let todoValue = self.attribute(.todo, at: range.location, effectiveRange: nil) as? Int else { return } let gfm = todoValue == 1 ? "- [x]" : "- [ ]" let adjustedRange = NSRange(location: range.location + offset, length: range.length) result.replaceCharacters(in: adjustedRange, with: gfm) offset += gfm.count - range.length } return result } public func loadTasks() { while mutableString.contains("- [ ] ") { let range = mutableString.range(of: "- [ ] ") if length >= range.upperBound, let unChecked = AttributedBox.getUnChecked() { replaceCharacters(in: range, with: unChecked) } } while mutableString.contains("- [x] ") { let range = mutableString.range(of: "- [x] ") let parRange = mutableString.paragraphRange(for: range) if length >= range.upperBound, let checked = AttributedBox.getChecked() { #if os(macOS) let color = UserDataService.instance.isDark ? NSColor.white : NSColor.black addAttribute(.strikethroughColor, value: color, range: parRange) addAttribute(.strikethroughStyle, value: 1, range: parRange) #else addAttribute(.strikethroughColor, value: UIColor.blackWhite, range: parRange) #endif replaceCharacters(in: range, with: checked) } } } public func loadFont() { addAttribute(.font, value: UserDefaultsManagement.noteFont, range: NSRange(location: 0, length: length)) } public func unloadAttachments() -> NSMutableAttributedString { return unloadTasks() .unloadImagesAndFiles() } public func loadAttachments(_ note: Note) -> NSMutableAttributedString { loadImagesAndFiles(note: note) loadTasks() return self } public func replaceTag(name: String, with replaceString: String) { let escapedName = NSRegularExpression.escapedPattern(for: name) let pattern = "(?<=^|\\s)\(escapedName)(?=$|\\s|/)" guard let regex = try? NSRegularExpression(pattern: pattern, options: [.caseInsensitive]) else { return } let fullRange = NSRange(location: 0, length: mutableString.length) let matches = regex.matches(in: mutableString as String, options: [], range: fullRange) for match in matches.reversed() { if replaceString.isEmpty { mutableString.replaceCharacters(in: match.range, with: "") } else { mutableString.replaceCharacters(in: match.range, with: replaceString) } } } public func getImagesAndFiles() -> [(url: URL, title: String, path: String)] { var res = [(url: URL, title: String, path: String)]() let fullRange = NSRange(location: 0, length: length) enumerateAttribute(.attachment, in: fullRange) { _, range, _ in guard let meta = getMeta(at: range.location) else { return } res.append(meta) } return res } public func getMeta(at location: Int) -> (url: URL, title: String, path: String)? { guard location >= 0 && location < self.length else { return nil } guard let url = attribute(.attachmentUrl, at: location, effectiveRange: nil) as? URL, let path = attribute(.attachmentPath, at: location, effectiveRange: nil) as? String else { return nil } let title = attribute(.attachmentTitle, at: location, effectiveRange: nil) as? String ?? String() return (url: url, title: title, path: path) } public func getData(at location: Int) -> Data? { guard location >= 0 && location < self.length else { return nil } var range = NSRange() if let data = attribute(.attachmentSave, at: location, effectiveRange: &range) as? Data { removeAttribute(.attachmentSave, range: range) return data } return nil } public func saveData() { let range = NSRange(location: 0, length: length) enumerateAttribute(.attachmentUrl, in: range) { (value, range, _) in guard let url = value as? URL, let data = try? Data(contentsOf: url) else { return } addAttribute(.attachmentSave, value: data, range: range) } } public static func build(data: Data, preferredName: String? = nil) -> NSMutableAttributedString? { var preferredName = preferredName if preferredName == nil { let ext = data.getFileType().rawValue preferredName = UUID().uuidString + "." + ext } let attachment = NSTextAttachment() let mutable = NSMutableAttributedString(attachment: attachment) mutable.addAttributes([ .attachmentSave: data, .attachmentUrl: URL(fileURLWithPath: "/tmp/" + preferredName!), .attachmentPath: String() ], range: NSRange(location: 0, length: 1)) mutable.append(NSAttributedString(string: "\n\n")) return mutable } } ================================================ FILE: FSNotesCore/Extensions/NSRange+.swift ================================================ // // NSRange+.swift // FSNotes // // Created by Oleksandr Hlushchenko on 12.10.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import Foundation extension NSRange { var upperBound: Int { location + length } func intersects(_ other: NSRange) -> Bool { return NSIntersectionRange(self, other).length > 0 } } ================================================ FILE: FSNotesCore/Extensions/NSTextCheckingResult+.swift ================================================ // // NSTextCheckingResult+.swift // FSNotes // // Created by Oleksandr Hlushchenko on 15.10.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import Foundation extension NSTextCheckingResult { func optionalRange(at idx: Int) -> NSRange? { let range = self.range(at: idx) return range.location != NSNotFound ? range : nil } } ================================================ FILE: FSNotesCore/Extensions/Pasteboard.swift ================================================ // // NSPasteboard+.swift // FSNotes // // Created by Олександр Глущенко on 25.09.2020. // Copyright © 2020 Oleksandr Glushchenko. All rights reserved. // #if os(macOS) import Cocoa extension NSPasteboard { public static var note: NSPasteboard.PasteboardType { .init("es.fsnot.pasteboard.note") } public static var project: NSPasteboard.PasteboardType { .init("es.fsnot.pasteboard.project") } public static var attributed: NSPasteboard.PasteboardType { .init("es.fsnot.pasteboard.attributed") } } #elseif os(iOS) import UIKit extension UIPasteboard { public static var attributed: String { "es.fsnot.pasteboard.attributed" } } #endif ================================================ FILE: FSNotesCore/Extensions/Project+Git.swift ================================================ // // Project+Git.swift // FSNotes // // Created by Oleksandr Hlushchenko on 31.10.2022. // Copyright © 2022 Oleksandr Hlushchenko. All rights reserved. // import Foundation extension Project { public func getGitOrigin() -> String? { if let origin = settings.gitOrigin, origin.count > 0 { return origin } return nil } #if os(OSX) public func getRepositoryUrl() -> URL { if UserDefaultsManagement.separateRepo && !isCloudProject() { return url.appendingPathComponent(".git", isDirectory: true) } let key = String(url.path.md5.prefix(4)) let repoURL = UserDefaultsManagement.gitStorage!.appendingPathComponent(key + " - " + label + ".git") return repoURL } #else public func getRepositoryUrl() -> URL { if !UserDefaultsManagement.iCloudDrive { return url.appendingPathComponent(".git") } let key = settingsKey.md5.prefix(6) let repoURL = UserDefaultsManagement.gitStorage!.appendingPathComponent(key + " - " + label + ".git") return repoURL } #endif public func hasRepository() -> Bool { let url = getRepositoryUrl() return FileManager.default.fileExists(atPath: url.path) } public func getGitProject() -> Project? { if hasRepository() { return self } if let parent = parent, let root = parent.getGitProject() { return root } return nil } public func initBareRepository() throws { let repositoryManager = RepositoryManager() let repoURL = getRepositoryUrl() // Prepare temporary dir let tempURL = UserDefaultsManagement.gitStorage!.appendingPathComponent("tmp") try? FileManager.default.removeItem(at: tempURL) try? FileManager.default.createDirectory(at: tempURL, withIntermediateDirectories: true) // Init let signature = Signature(name: "FSNotes App", email: "support@fsnot.es") let repository = try repositoryManager.initRepository(at: tempURL, signature: signature) if isUseWorkTree() { repository.setWorkTree(path: url.path) } let dotGit = tempURL.appendingPathComponent(".git") if FileManager.default.directoryExists(atUrl: dotGit) { try? FileManager.default.moveItem(at: dotGit, to: repoURL) } } public func cloneRepository() throws -> Repository? { let repositoryManager = RepositoryManager() let repoURL = getRepositoryUrl() // Prepare temporary dir guard let tempURL = UserDefaultsManagement.gitStorage?.appendingPathComponent("tmp") else { return nil } try? FileManager.default.removeItem(at: tempURL) try? FileManager.default.createDirectory(at: tempURL, withIntermediateDirectories: true) // Clone if let originString = getGitOrigin(), let origin = URL(string: originString) { let repository = try repositoryManager.cloneRepository(from: origin, at: tempURL, authentication: getAuthHandler()) if isUseWorkTree() { repository.setWorkTree(path: url.path) } let dotGit = tempURL.appendingPathComponent(".git") if FileManager.default.directoryExists(atUrl: dotGit) { try? FileManager.default.moveItem(at: dotGit, to: repoURL) return try repositoryManager.openRepository(at: repoURL) } return nil } return nil } public func getRepository() throws -> Repository { let repositoryManager = RepositoryManager() let repoURL = getRepositoryUrl() return try repositoryManager.openRepository(at: repoURL) } public func useSeparateRepo() -> Bool { return UserDefaultsManagement.separateRepo && !isCloudProject() } public func isCloudProject() -> Bool { guard let storagePath = UserDefaultsManagement.storagePath, let documentsProject = UserDefaultsManagement.iCloudDocumentsContainer else { return false } if storagePath == documentsProject.path, url.path.contains(storagePath) { return true } return false } public func getAuthHandler() -> SshKeyHandler? { var rsa: URL? if let rsaURL = installSSHKey() { rsa = rsaURL } guard let rsaURL = rsa else { return nil } let passphrase = settings.gitPrivateKeyPassphrase ?? "" let sshKeyDelegate = StaticSshKeyDelegate(privateUrl: rsaURL, passphrase: passphrase) let handler = SshKeyHandler(sshKeyDelegate: sshKeyDelegate) return handler } public func getSSHKeyUrl() -> URL? { let keyName = getSettingsKey() return storage .getGitKeysDir()? .appendingPathComponent(keyName) } public func removeSSHKey() { guard let url = getSSHKeyUrl() else { return } try? FileManager.default.removeItem(at: url) try? FileManager.default.removeItem(at: url.appendingPathExtension("pub")) } public func installSSHKey() -> URL? { guard let url = getSSHKeyUrl() else { return nil } if let key = settings.gitPrivateKey { do { try key.write(to: url) if let publicKey = settings.gitPublicKey { let publicKeyUrl = url.appendingPathExtension("pub") try publicKey.write(to: publicKeyUrl) } return url } catch {/*_*/} } return nil } public func getSign() -> Signature { return Signature(name: "FSNotes App", email: "support@fsnot.es") } public func commit(message: String? = nil, progress: GitProgress? = nil) throws { let repository = try getRepository() let lastCommit = try? repository.head().targetCommit() // Add all and save index let head = try repository.head().index() if let progress = progress { progress.log(message: "git add .") } let success = head.add(path: ".") // No commits yet or added files was found if success || lastCommit == nil { try head.save() do { progress?.log(message: "git commit") let sign = getSign() if lastCommit == nil { let commitMessage = message ?? "FSNotes Init" _ = try head.createInitialCommit(msg: commitMessage, signature: sign) } else { let commitMessage = message ?? "Usual commit" _ = try head.createCommit(msg: commitMessage, signature: sign) } progress?.log(message: "git commit done 🤟") cacheHistory(progress: progress) } catch { progress?.log(message: "commit error: \(error)") } } else { progress?.log(message: "git add: no new data") throw GitError.noAddedFiles } } public func checkGitState() throws -> Bool { let repository = try getRepository() let statuses = Statuses(repository: repository) isCleanGit = statuses.workingDirectoryClean return isCleanGit } public func getLocalBranch(repository: Repository) -> Branch? { do { let names = try Branches(repository: repository).names(type: .local) guard names.count > 0 else { return nil } guard let branchName = names.first?.components(separatedBy: "/").last else { return nil } let localMaster = try repository.branches.get(name: branchName) return localMaster } catch {/**/} return nil } public func push(progress: GitProgress? = nil) throws { guard let origin = getGitOrigin() else { return } let repository = try getRepository() repository.addRemoteOrigin(path: origin) let handler = getAuthHandler() let names = try Branches(repository: repository).names(type: .local) guard names.count > 0 else { return } guard let branchName = names.first?.components(separatedBy: "/").last else { return } let localMaster = try repository.branches.get(name: branchName) try repository.remotes.get(remoteName: "origin").push(local: localMaster, authentication: handler) if let progress = progress { progress.log(message: "\(label) – successful push 👌") } } public func pull(progress: GitProgress? = nil) throws { guard let origin = getGitOrigin() else { return } let repository = try getRepository() repository.addRemoteOrigin(path: origin) if isUseWorkTree() { repository.setWorkTree(path: url.path) } let authHandler = getAuthHandler() let sign = getSign() let remote = repository.remotes let remoteBranch = try remote.get(remoteName: "origin") do { try remoteBranch.pull(signature: sign, authentication: authHandler, project: self) } catch GitError.uncommittedConflict { try commit() try remoteBranch.pull(signature: sign, authentication: authHandler, project: self) try push() } if let progress = progress { progress.log(message: "\(label) – successful git pull 👌") } } public func isUseWorkTree() -> Bool { #if os(iOS) return UserDefaultsManagement.iCloudDrive #else return !UserDefaultsManagement.separateRepo || isCloudProject() #endif } public func isGitOriginExist() -> Bool { if let origin = settings.gitOrigin, origin.count > 0 { return true } return false } public func removeRepository(progress: GitProgress? = nil) { let repoURL = getRepositoryUrl() if FileManager.default.fileExists(atPath: repoURL.path) { try? FileManager.default.removeItem(at: repoURL) } removeCommitsCache() progress?.log(message: "git repository has been deleted") } public func removeCommitsCache() { if let url = getCommitsDiffsCache() { try? FileManager.default.removeItem(at: url) } } public func loadCommitsCache() { if !commitsCache.isEmpty { return } if let commitsDiffCache = getCommitsDiffsCache(), let data = try? Data(contentsOf: commitsDiffCache), let result = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSDictionary.self, NSArray.self, NSString.self], from: data) as? [String: [String]] { commitsCache = result } } public func cacheHistory(progress: GitProgress? = nil) { progress?.log(message: "git history caching ...") guard let repository = try? getRepository() else { return } do { let fileRevLog = try FileHistoryIterator(repository: repository, path: "Test", project: self) fileRevLog.walkCacheDiff() let cacheData = try? NSKeyedArchiver.archivedData(withRootObject: commitsCache, requiringSecureCoding: true) if let data = cacheData, let writeTo = getCommitsDiffsCache() { do { try data.write(to: writeTo) } catch { print("Caching error: " + error.localizedDescription) } } } catch { print(error) } progress?.log(message: "git history caching done 🤟") } public func getCommitsDiffsCache() -> URL? { guard let documentDir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { return nil } let fileName = "commitsDiff-\(settingsKey).cache" return documentDir.appendingPathComponent(fileName, isDirectory: false) } public func hasCommitsDiffsCache() -> Bool { guard let project = getGitProject() else { return false } if let url = project.getCommitsDiffsCache() { return FileManager.default.fileExists(atPath: url.path) } return false } public func getRepositoryState() -> RepositoryAction { if hasRepository() { if settings.gitOrigin != nil { return .pullPush } else { return .commit } } else { if settings.gitOrigin != nil { return .clonePush } else { return .initCommit } } } public func gitDo(_ action: RepositoryAction, progress: GitProgress? = nil) -> String? { var message: String? do { switch action { case .initCommit: try initBareRepository() try commit(message: nil, progress: progress) case .clonePush: removeCommitsCache() message = clonePush(progress: progress) case .commit: try commit(message: nil, progress: progress) case .pullPush: do { try pull(progress: progress) try push(progress: progress) } catch GitError.notFound(let ref) { progress?.log(message: "\(ref) not found, push trying ...") try push(progress: progress) } } } catch { if let error = error as? GitError { message = error.associatedValue() } else { message = error.localizedDescription } } return message } private func clonePush(progress: GitProgress? = nil) -> String? { var message: String? do { if let repo = try cloneRepository(), let local = getLocalBranch(repository: repo) { try repo.head().checkout(branch: local, type: .force) cacheHistory(progress: progress) } else { do { try commit(message: nil, progress: progress) try push(progress: progress) } catch { message = error.localizedDescription } } } catch GitError.unknownError(let errorMessage, _, let desc) { message = errorMessage + " – " + desc } catch GitError.notFound(let ref) { // Empty repository – commit and push if ref == "refs/heads/master" { do { try commit(message: nil, progress: progress) try push(progress: progress) } catch { message = error.localizedDescription } } } catch { message = error.localizedDescription } return message } public func saveRevision(commitMessage: String? = nil) throws { try commit(message: commitMessage) // No hands – no mults guard getGitOrigin() != nil else { return } try pull() try push() } } ================================================ FILE: FSNotesCore/Extensions/Storage+Git.swift ================================================ // // File.swift // FSNotes // // Created by Oleksandr Hlushchenko on 17.02.2023. // Copyright © 2023 Oleksandr Hlushchenko. All rights reserved. // import Foundation extension Storage { public func pullAll(force: Bool = false) { guard let projects = getGitProjects() else { return } for project in projects { #if os(iOS) if !force && !project.settings.gitAutoPull { continue } #endif var status: String? do { try project.pull() let currentDate = Date() let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd HH:mm" let dateString = dateFormatter.string(from: currentDate) status = "Successfull auto pull – \(dateString)" project.gitStatus = status } catch { if let error = error as? GitError { status = error.associatedValue() } } #if os(iOS) if let status = status { print(status) project.gitStatus = status if let viewController = AppDelegate.getGitVCOptional(for: project) { viewController.setProgress(message: status) } } #endif } } public func checkGitState() { guard let projects = getGitProjects() else { return } for project in projects { do { _ = try project.checkGitState() } catch { if let error = error as? GitError { project.gitStatus = error.associatedValue() } } } } } ================================================ FILE: FSNotesCore/Extensions/String+.swift ================================================ // // String+.swift // FSNotes // // Created by Jeff Hanbury on 29/08/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Foundation import CommonCrypto #if os(OSX) import Cocoa #else import UIKit #endif public extension String { #if os(OSX) typealias Font = NSFont #else typealias Font = UIFont #endif func condenseWhitespace() -> String { let components = self.components(separatedBy: NSCharacterSet.whitespacesAndNewlines) return components.filter { !$0.isEmpty }.joined(separator: " ") } // Search the string for the existence of any of the terms in the provided array of terms. // Inspired by magic from https://stackoverflow.com/a/41902740/2778502 func localizedStandardContains(_ terms: [S]) -> Bool { return terms.first(where: { self.localizedStandardContains($0) }) != nil } func trim() -> String { return self.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines) } func trimSpaces() -> String { return self.trimmingCharacters(in: NSCharacterSet.whitespaces) } func trimMDSyntax() -> String { return self .replacingOccurrences(of: "###### ", with: " ") .replacingOccurrences(of: "##### ", with: " ") .replacingOccurrences(of: "#### ", with: " ") .replacingOccurrences(of: "### ", with: " ") .replacingOccurrences(of: "## ", with: " ") .replacingOccurrences(of: "# ", with: " ") .replacingOccurrences(of: "```", with: "") .replacingOccurrences(of: "- [ ]", with: "") .replacingOccurrences(of: "- [x]", with: "") .replacingOccurrences(of: "[[", with: "") .replacingOccurrences(of: "]]", with: "") .replacingOccurrences(of: "{{TOC}}", with: "") .replacingOccurrences(of: "\u{FFFC}", with: "") } func getPrefixMatchSequentially(char: String) -> String? { var result = String() for current in self { if current.description == char { result += char continue } break } if result.count > 0 { return result } return nil } func localizedCaseInsensitiveContainsTerms(_ terms: [Substring]) -> Bool { // Use magic from https://stackoverflow.com/a/41902740/2778502 return terms.first(where: { !self.localizedLowercase.contains($0) }) == nil } func removeLastNewLine() -> String { if self.last == "\n" { return String(self.dropLast()) } return self } func isValidEmail() -> Bool { let pattern = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" if let regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive) { return regex.firstMatch(in: self, options: [], range: NSRange(location: 0, length: count)) != nil } return false } var isValidUUID: Bool { return UUID(uuidString: self) != nil } var md5: String { let data = Data(self.utf8) let hash = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> [UInt8] in var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH)) CC_MD5(bytes.baseAddress, CC_LONG(data.count), &hash) return hash } return hash.map { String(format: "%02x", $0) }.joined() } var isWhitespace: Bool { guard !isEmpty else { return true } let whitespaceChars = NSCharacterSet.whitespacesAndNewlines return self.unicodeScalars .filter { (unicodeScalar: UnicodeScalar) -> Bool in !whitespaceChars.contains(unicodeScalar) } .count == 0 } var isNumber: Bool { return !isEmpty && rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil } var isContainsLetters: Bool { let letters = CharacterSet.letters return self.rangeOfCharacter(from: letters) != nil } var fnv1a: UInt64 { let fnvOffset: UInt64 = 0xcbf29ce484222325 let fnvPrime: UInt64 = 0x100000001b3 var hash = fnvOffset for byte in self.utf8 { hash ^= UInt64(byte) hash = hash &* fnvPrime } return hash } var withoutSpecialCharacters: String { return self.components(separatedBy: CharacterSet.alphanumerics.inverted).joined(separator: " ") .condenseWhitespace() } func escapePlus() -> String { return self.replacingOccurrences(of: "+", with: "%20") } func matchingStrings(regex: String) -> [[String]] { guard let regex = try? NSRegularExpression(pattern: regex, options: [.dotMatchesLineSeparators]) else { return [] } let nsString = self as NSString let results = regex.matches(in: self, options: [], range: NSRange(0.. String { let result = self .replacingOccurrences(of: ":", with: "") .replacingOccurrences(of: "/", with: "") .replacingOccurrences( of: #"^#{1,6} "#, with: "", options: .regularExpression ) return (result.count > length) ? String(result.prefix(length)) : result } func startsWith(string: String) -> Bool { guard let range = range(of: string, options: [.caseInsensitive, .diacriticInsensitive]) else { return false } return range.lowerBound == startIndex } func widthOfString(usingFont font: Font, tabs: [NSTextTab]? = nil) -> CGFloat { let paragraph = NSMutableParagraphStyle() if let tabs = tabs { paragraph.tabStops = tabs } let fontAttributes = [ NSAttributedString.Key.font: font, NSAttributedString.Key.paragraphStyle: paragraph ] let size = self.size(withAttributes: fontAttributes) return size.width } func getSpacePrefix() -> String { var prefix = String() for char in unicodeScalars { if char == "\t" || char == " " { prefix += String(char) } else { break } } return prefix } func substring(with nsRange: NSRange) -> Substring? { guard let range = Range(nsRange, in: self) else { return nil } return self[range] } func replaced(from: String, to: String, by new: String) -> String { guard let from = range(of: from)?.lowerBound, let to = range(of: to)?.upperBound else { return self } let range = from.. String { let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" var randomString: String = "" for _ in 0.. String { if let decodedString = self.applyingTransform(.stripCombiningMarks, reverse: true) { return decodedString } return self } func isHexColor() -> Bool { return self.count == 6 && self.allSatisfy({ $0.isHexDigit }) } private func index(at location: Int) -> String.Index? { return self.index(startIndex, offsetBy: location, limitedBy: endIndex) } func capitalizingFirstLetter() -> String { guard !isEmpty else { return self } return prefix(1).uppercased() + dropFirst() } func createURL(for note: Note? = nil) -> URL? { var normalizedPath = self // Expand ~ to user home directory if normalizedPath.hasPrefix("~") { normalizedPath = "/Users/\(NSUserName())" + normalizedPath.dropFirst() } // Handle absolute paths if normalizedPath.hasPrefix("/") { return URL(fileURLWithPath: normalizedPath) } if let note = note, normalizedPath.hasPrefix("./") { normalizedPath = note.project.url.path + normalizedPath.dropFirst() return URL(fileURLWithPath: normalizedPath) } // Handle regular URLs return URL(string: normalizedPath) } func substring(nsRange: NSRange) -> String? { guard let range = Range(nsRange, in: self) else { return nil } return String(self[range]) } func countWords() -> Int { var count = 0 var inWord = false for b in self.utf8 { if b == 32 || b == 10 || b == 9 || b == 13 { inWord = false } else { if !inWord { count += 1 inWord = true } } } return count } func countChars() -> Int { if self.utf8.allSatisfy({ $0 < 0x80 }) { return self.utf8.count } return self.count } } extension StringProtocol where Index == String.Index { public func nsRange(from range: Range) -> NSRange { return NSRange(range, in: self) } } public extension String { subscript(value: Int) -> Character { self[index(at: value)] } } public extension String { subscript(value: NSRange) -> Substring { self[value.lowerBound..) -> Substring { self[index(at: value.lowerBound)...index(at: value.upperBound)] } subscript(value: CountableRange) -> Substring { self[index(at: value.lowerBound)..) -> Substring { self[..) -> Substring { self[...index(at: value.upperBound)] } subscript(value: PartialRangeFrom) -> Substring { self[index(at: value.lowerBound)...] } } private extension String { func index(at offset: Int) -> String.Index { index(startIndex, offsetBy: offset) } } ================================================ FILE: FSNotesCore/Extensions/String+Punycode.swift ================================================ // // String+Punycode.swift // FSNotes // // Created by Александр on 30.01.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // import Foundation import Punycode extension String { func idnaEncodeURL() -> String { if URL(string: self) != nil { return self } var scheme: String? var host: String? var path: String? var url = self if url.startsWith(string: "https://") { url = String(url.dropFirst(8)) scheme = "https" } if url.startsWith(string: "http://") { url = String(url.dropFirst(7)) scheme = "http" } guard let scheme = scheme else { return self } if url.contains("/") { let parts = url.components(separatedBy: "/") if parts[0].contains(".") { host = parts[0] path = parts.dropFirst().joined(separator: "/") } } else if url.contains(".") { host = url } guard let host = host else { return self } let parts = host.components(separatedBy: ".").compactMap({ $0.idnaEncoded! }) let domain = parts.joined(separator: ".") let encodedPath = getEncodedPath(path: path) let result = String("\(scheme)://\(domain)/\(encodedPath)") return result } private func getEncodedPath(path: String?) -> String { var unwrappedPath = String() if let path = path { var addPercentEncoding = false for pathChar in path.unicodeScalars { if !CharacterSet.urlPathAllowed.contains(pathChar) { addPercentEncoding = true } } if addPercentEncoding, let pathPercentEncoded = path.removingPercentEncoding?.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) { unwrappedPath = pathPercentEncoded } else { unwrappedPath = path } } return unwrappedPath } } ================================================ FILE: FSNotesCore/Extensions/URL+.swift ================================================ // // URL+.swift // FSNotes // // Created by Oleksandr Glushchenko on 3/22/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation #if os(iOS) import MobileCoreServices #else import CoreServices #endif public extension URL { /// Get extended attribute. func extendedAttribute(forName name: String) throws -> Data { return try self.withUnsafeFileSystemRepresentation { fileSystemPath -> Data in // Determine attribute size: let length = getxattr(fileSystemPath, name, nil, 0, 0, 0) guard length >= 0 else { throw URL.posixError(errno) } // Create buffer with required size: var data = Data(count: length) let count = data.count // Retrieve attribute: let result = data.withUnsafeMutableBytes { getxattr(fileSystemPath, name, $0.baseAddress, count, 0, 0) } guard result >= 0 else { throw URL.posixError(errno) } return data } } /// Set extended attribute. func setExtendedAttribute(data: Data, forName name: String) throws { try self.withUnsafeFileSystemRepresentation { fileSystemPath in let result = data.withUnsafeBytes { setxattr(fileSystemPath, name, $0.baseAddress, data.count, 0, 0) } guard result == 0 else { throw URL.posixError(errno) } } } /// Remove extended attribute. func removeExtendedAttribute(forName name: String) throws { try self.withUnsafeFileSystemRepresentation { fileSystemPath in let result = removexattr(fileSystemPath, name, 0) guard result == 0 else { throw URL.posixError(errno) } } } /// Get list of all extended attributes. func listExtendedAttributes() throws -> [String] { let list = try self.withUnsafeFileSystemRepresentation { fileSystemPath -> [String] in let length = listxattr(fileSystemPath, nil, 0, 0) guard length >= 0 else { throw URL.posixError(errno) } // Create buffer with required size: var namebuf = [CChar](repeating: 0, count: length) // Retrieve attribute list: let result = listxattr(fileSystemPath, &namebuf, namebuf.count, 0) guard result >= 0 else { throw URL.posixError(errno) } // Extract attribute names: let list = namebuf.split(separator: 0).compactMap { $0.withUnsafeBufferPointer { $0.withMemoryRebound(to: UInt8.self) { String(bytes: $0, encoding: .utf8) } } } return list } return list } /// Helper function to create an NSError from a Unix errno. private static func posixError(_ err: Int32) -> NSError { return NSError(domain: NSPOSIXErrorDomain, code: Int(err), userInfo: [NSLocalizedDescriptionKey: String(cString: strerror(err))]) } // Access the URL parameters eg nv://make?title=blah&txt=body like so: // let titleStr = myURL['title'] subscript(queryParam: String) -> String? { guard let url = URLComponents(string: self.absoluteString) else { return nil } return url.queryItems?.first(where: { $0.name == queryParam })?.value } func isRemote() -> Bool { return (self.absoluteString.starts(with: "http://") || self.absoluteString.starts(with: "https://")) } func isHidden() -> Bool { if let data = try? extendedAttribute(forName: "es.fsnot.hidden.dir"), String(data: data, encoding: .utf8) == "true" { return true } return false } func hasNonHiddenBit() -> Bool { if let data = try? extendedAttribute(forName: "es.fsnot.hidden.dir"), String(data: data, encoding: .utf8) == "false" { return true } return false } var attributes: [FileAttributeKey: Any]? { do { return try FileManager.default.attributesOfItem(atPath: path) } catch _ as NSError { //print("FileAttribute error: \(error)") } return nil } var fileSize: UInt64 { return attributes?[.size] as? UInt64 ?? UInt64(0) } func removingFragment() -> URL { var string = self.absoluteString if let query = query { string = string.replacingOccurrences(of: "?\(query)", with: "") } if let fragment = fragment { string = string.replacingOccurrences(of: "#\(fragment)", with: "") } return URL(string: string) ?? self } var typeIdentifier: String? { return (try? resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier } var fileUTType: CFString? { let unmanagedFileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil) return unmanagedFileUTI?.takeRetainedValue() } var isVideo: Bool { guard let fileUTI = fileUTType else { return false } return UTTypeConformsTo(fileUTI, kUTTypeMovie) || UTTypeConformsTo(fileUTI, kUTTypeVideo) || UTTypeConformsTo(fileUTI, kUTTypeQuickTimeMovie) || UTTypeConformsTo(fileUTI, kUTTypeMPEG) || UTTypeConformsTo(fileUTI, kUTTypeMPEG2Video) || UTTypeConformsTo(fileUTI, kUTTypeMPEG2TransportStream) || UTTypeConformsTo(fileUTI, kUTTypeMPEG4) || UTTypeConformsTo(fileUTI, kUTTypeAppleProtectedMPEG4Video) || UTTypeConformsTo(fileUTI, kUTTypeAVIMovie) } var isImage: Bool { guard let fileUTI = fileUTType else { return false } return UTTypeConformsTo(fileUTI, kUTTypeImage) } var isMedia: Bool { return isImage || isVideo } var mimeType: String { guard let identifier = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(), let mimeType = UTTypeCopyPreferredTagWithClass(identifier, kUTTagClassMIMEType)?.takeRetainedValue() as String? else { return "application/octet-stream" } return mimeType } var isWebURL: Bool { guard let scheme = scheme?.lowercased() else { return false } return scheme == "http" || scheme == "https" } } ================================================ FILE: FSNotesCore/Extensions/URL+Image.swift ================================================ // // URL+Image.swift // FSNotes // // Created by Oleksandr Hlushchenko on 15.11.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import Foundation import AVKit extension URL { func getImageSize() -> CGSize { guard let imageSource = CGImageSourceCreateWithURL(self as CFURL, nil), let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [CFString: Any] else { return .zero } var width = properties[kCGImagePropertyPixelWidth] as? Int ?? 0 var height = properties[kCGImagePropertyPixelHeight] as? Int ?? 0 let orientation = properties[kCGImagePropertyOrientation] as? Int ?? 0 // Swap dimensions for rotated images if case 5...8 = orientation { swap(&width, &height) } return CGSize(width: width, height: height) } func getVideoSize() -> CGSize { guard let track = AVURLAsset(url: self).tracks(withMediaType: .video).first else { return .zero } let size = track.naturalSize.applying(track.preferredTransform) return CGSize(width: abs(size.width), height: abs(size.height)) } func getMediaSize() -> CGSize { if isVideo { return getVideoSize() } return getImageSize() } func getBorderSize(maxWidth: CGFloat) -> CGSize { let size = getMediaSize() guard size.width > maxWidth else { return size } let scale = maxWidth / size.width let newHeight = size.height * scale return CGSize(width: maxWidth, height: newHeight) } } ================================================ FILE: FSNotesCore/Extensions/UTI.swift ================================================ import Foundation #if os(OSX) import CoreServices #elseif os(iOS) import MobileCoreServices #endif public extension String { func tag(withClass: CFString) -> String? { return UTTypeCopyPreferredTagWithClass(self as CFString, withClass)?.takeRetainedValue() as String? } func uti(withClass: CFString) -> String? { return UTTypeCreatePreferredIdentifierForTag(withClass, self as CFString, nil)?.takeRetainedValue() as String? } var utiMimeType: String? { return tag(withClass: kUTTagClassMIMEType) } var mimeTypeUTI: String? { return uti(withClass: kUTTagClassMIMEType) } var fileExtensionUTI: String? { return uti(withClass: kUTTagClassFilenameExtension) } } ================================================ FILE: FSNotesCore/FSParser.swift ================================================ // // FSParser.swift // FSNotes // // Created by Александр on 30.01.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // import Foundation class FSParser { /* --- key: value --- YAML */ public static let yamlBlockPattern = [ "(?<=\\n|\\A)", "(^---(([^\\n]*?)\\n(?!\\n))+---)", "(?:\\n|\\Z)" ].joined(separator: "\n") public static let tagsPattern = ###""" (?:\A|\s|[^\]]\() \#( [^ \s # no whitespace \# # no hashes \+ # no plus sign https://github.com/glushchenko/fsnotes/issues/1303 ,?!"`';:\. # no punctuation \\ # no backslash (){}\[\] # no bracket pairs ]+ ) """### /* ``` Code ``` Code */ public static let codeQuoteBlockPattern = [ "(?<=\\n|\\A)", "(^```[\\S\\ \\(\\)]*\\n([\\s\\S]*?)\\n```(?:\\n|\\Z))" ].joined(separator: "\n") public static let codeSpanPattern = [ "(? String { // in other words (this) and (this(also)) and (this(also(too))) // up to _nestDepth if nestedParensPattern.isEmpty { nestedParensPattern = repeatString([ "(?> # Atomic matching", "[^()\\s]+ # Anything other than parens or whitespace", "|", "\\(" ].joined(separator: "\n"), 6) + repeatString(" \\))*", 6) } return nestedParensPattern } /// this is to emulate what's available in PHP public static func repeatString(_ text: String, _ count: Int) -> String { return Array(repeating: text, count: count).reduce("", +) } public static let yamlBlockRegex = FSParserRegex(pattern: yamlBlockPattern, options: [.allowCommentsAndWhitespace, .dotMatchesLineSeparators]) public static let imageInlineRegex = FSParserRegex(pattern: imageInlinePattern, options: [.allowCommentsAndWhitespace, .dotMatchesLineSeparators]) public static let tagsInlineRegex = FSParserRegex(pattern: tagsPattern, options: [.allowCommentsAndWhitespace, .anchorsMatchLines]) public static func getSpanCodeBlockRange(content: NSMutableAttributedString, range: NSRange) -> NSRange? { var codeSpan: NSRange? let paragraphRange = content.mutableString.paragraphRange(for: range) let paragraph = content.attributedSubstring(from: paragraphRange).string if paragraph.contains("`") { FSParserRegex(pattern: codeSpanPattern, options: [.allowCommentsAndWhitespace, .dotMatchesLineSeparators]).matches(content.string, range: paragraphRange) { (result) -> Void in if let spanRange = result?.range, spanRange.intersection(range) != nil { codeSpan = spanRange } } } return codeSpan } } public struct FSParserRegex { public let regularExpression: NSRegularExpression! public init(pattern: String, options: NSRegularExpression.Options = NSRegularExpression.Options(rawValue: 0)) { var error: NSError? let regexp: NSRegularExpression? do { regexp = try NSRegularExpression(pattern: pattern, options: options) } catch let error1 as NSError { error = error1 regexp = nil } // If re is nil, it means NSRegularExpression didn't like // the pattern we gave it. All regex patterns used by Markdown // should be valid, so this probably means that a pattern // valid for .NET Regex is not valid for NSRegularExpression. if regexp == nil { if let error = error { print("Regular expression error: \(error.userInfo)") } assert(regexp != nil) } self.regularExpression = regexp } public func matches(_ input: String, range: NSRange, completion: @escaping (_ result: NSTextCheckingResult?) -> Void) { let sInput = input as NSString let options = NSRegularExpression.MatchingOptions(rawValue: 0) regularExpression.enumerateMatches(in: sInput as String, options: options, range: range, using: {(result, _, _) -> Void in completion(result) }) } } ================================================ FILE: FSNotesCore/Git/authentication/Authentication.swift ================================================ // // Authentication.swift // Git2Swift // // Created by Damien Giron on 20/08/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 /// Authentication handler public protocol AuthenticationHandler { /// Raw libgit2 authentication /// /// - parameter out: Git credential /// - parameter url: url /// - parameter username: username /// /// - returns: 0 on ok func authenticate(out: UnsafeMutablePointer?>?, url: String?, username: String?) -> Int32 } func setAuthenticationCallback(_ callbacksStruct: inout git_remote_callbacks, authentication: AuthenticationHandler?) { // Convert handler to payload pointer callbacksStruct.payload = Unmanaged .passRetained(CWrapper(authentication)) .toOpaque() // Create crdential lambda calling credential handler callbacksStruct.credentials = { out, url, username_from_url, allowed_types, payload in // Find url let sUrl : String? if (url == nil) { sUrl = nil } else { sUrl = git_string_converter(url!) } // Find username_from_url let userName : String? if (username_from_url == nil) { userName = nil } else { userName = git_string_converter(username_from_url!) } // Transformation du pointer en wrapper let authenticationWrapper = Unmanaged> .fromOpaque(payload!) .takeRetainedValue() let result = authenticationWrapper.object.authenticate(out: out, url: sUrl, username: userName) return result } } ================================================ FILE: FSNotesCore/Git/authentication/KeyAuthentication.swift ================================================ // // KeyAuthentication.swift // Git2Swift // // Created by Damien Giron on 11/09/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 public protocol SshKeyData { /// Find user name var username : String? { get } /// Find private key URL var privateKey : URL { get } /// Key pass phrase (may be nil) var passphrase : String? { get } var publicKey : URL? { get } } public struct RawSshKeyData : SshKeyData { public let username : String? public let privateKey : URL public let publicKey : URL? public let passphrase : String? public init(username : String?, privateKey : URL, publicKey: URL? = nil, passphrase : String? = nil) { self.username = username self.privateKey = privateKey self.publicKey = publicKey self.passphrase = passphrase } } /// Ssh key delegate public protocol SshKeyDelegate { /// Get ssh key data /// /// - parameter username: username /// - parameter url: url /// /// - returns: Password data func get(username: String?, url: URL?) -> SshKeyData } /// SSh authentication public class SshKeyHandler : AuthenticationHandler { /// Delegate to access SSH key private let sshKeyDelegate: SshKeyDelegate public init(sshKeyDelegate: SshKeyDelegate) { self.sshKeyDelegate = sshKeyDelegate } /// Authentication /// /// - parameter out: Git credential /// - parameter url: url /// - parameter username: username /// /// - returns: 0 on ok public func authenticate(out: UnsafeMutablePointer?>?, url: String?, username: String?) -> Int32 { // Find ssh key data let sshKeyData = sshKeyDelegate.get(username: username, url: url == nil ? nil : URL(string: url!)) // Private key let privateKey = sshKeyData.privateKey.path if (FileManager.default.fileExists(atPath: privateKey) == false) { return 2 } // User name let optionalUsername = sshKeyData.username let optionalPassPhrase = sshKeyData.passphrase let publicKey = sshKeyData.publicKey != nil ? sshKeyData.publicKey!.path : nil return git_cred_ssh_key_new(out, optionalUsername == nil ? "" : optionalUsername!, publicKey, privateKey, optionalPassPhrase == nil ? "" : optionalPassPhrase!) } } ================================================ FILE: FSNotesCore/Git/authentication/PasswordAuthentication.swift ================================================ // // PasswordAuthentication.swift // Git2Swift // // Created by Damien Giron on 11/09/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 public protocol PasswordData { var username : String? { get } var password : String? { get } } public struct RawPasswordData : PasswordData { public let username : String? public let password : String? public init(username : String?, password : String?) { self.username = username self.password = password } } /// Password delegate public protocol PasswordDelegate { /// Get password data /// /// - parameter username: username /// - parameter url: url /// /// - returns: Password data func get(username: String?, url: URL?) -> PasswordData } /// Password handler public class PasswordHandler : AuthenticationHandler { /// Password delegate private let passwordDelegate : PasswordDelegate public init(passwordDelegate: PasswordDelegate) { self.passwordDelegate = passwordDelegate } /// Authentication /// /// - parameter out: Git credential /// - parameter url: url /// - parameter username: user name /// /// - returns: 0 on ok public func authenticate(out: UnsafeMutablePointer?>?, url: String?, username: String?) -> Int32 { // Find password data let passwordData = passwordDelegate.get(username: username, url: url == nil ? nil : URL(string: url!)) let optionalUsername = passwordData.username let optionalPasword = passwordData.password // Auth plain text return git_cred_userpass_plaintext_new( out, optionalUsername == nil ? "" : optionalUsername!, optionalPasword == nil ? "" : optionalPasword!) } } ================================================ FILE: FSNotesCore/Git/branch/Branch.swift ================================================ // // Branch.swift // Git2Swift // // Created by Damien Giron on 01/08/2016. // // import Foundation import Cgit2 /// Branch type /// /// - local: Local branch /// - remote: Remote branch /// - all: Local and remote branch public enum BranchType : UInt32 { case local = 1 // GIT_BRANCH_LOCAL case remote = 2 // GIT_BRANCH_REMOTE case all = 3 // GIT_BRANCH_ALL } /// Convert a Git2Swift branch to a libgit2 branch type /// /// - parameter type: Git2Swift branch /// /// - returns: libgit2 branch func git_convert_branch_type(_ type: BranchType) -> git_branch_t { switch type { case .local: return GIT_BRANCH_LOCAL case .remote: return GIT_BRANCH_REMOTE case .all: return GIT_BRANCH_ALL } } /// Convert a libgit2 branch to Git2Swift branch. /// /// - parameter type: Libgit2 branch /// /// - returns: Git2Swift branch func git_convert_from_git_branch_t(_ type: git_branch_t) -> BranchType { switch(type) { case GIT_BRANCH_LOCAL : return BranchType.local case GIT_BRANCH_REMOTE : return BranchType.remote default : return BranchType.all } } /// Define a git branch public class Branch : Reference { /// Type of branch public let type: BranchType lazy public var shortName : String = { do { return try Branch.getSpecInfo(spec: self.name, type: self.type).name } catch { return self.name } } () /// Decode rbanch spec in name and type /// /// - parameter spec: Full spec /// - parameter type: Known type for optimisation (may be null) /// /// - throws: GitError /// /// - returns: Typle (name, type) public static func getSpecInfo(spec: String, type: BranchType? = nil) throws -> (name: String, type: BranchType) { // Test local if ((type != nil && type! == .local) || spec.hasPrefix("refs/heads/")) { let startIndex = spec.index(spec.startIndex, offsetBy: 11) let name = String(spec[startIndex...]) return (name, BranchType.local) // Test remote } else if ((type != nil && type! == .remote) || spec.hasPrefix("refs/remotes/")) { let startIndex = spec.index(spec.startIndex, offsetBy: 13) let name = String(spec[startIndex...]) return (name, BranchType.local) } else { throw GitError.invalidSpec(spec: spec) } } /// Constructor with repository, name and libgit2 pointer /// /// - parameter repository: Git2Swift repository /// - parameter name: Name of the branch /// - parameter pointer: libgit2 reference pointer /// /// - throws: GitError on init error /// /// - returns: Git2Swift branch override init(repository: Repository, name: String, pointer: UnsafeMutablePointer) throws { // Set type if (git_reference_is_remote(pointer.pointee) == 1) { type = .remote } else { type = .local } try super.init(repository: repository, name: name, pointer: pointer) } /// Constructor with repository, libgit2 pointer and type. /// /// - parameter repository: Git2Swift repository /// - parameter pointer: libgit2 reference pointer /// - parameter type: Branch type /// /// - throws: GitError on init error /// /// - returns: Git2Swift branch init(repository: Repository, pointer: UnsafeMutablePointer, type: BranchType) throws { self.type = type // Read name from pointer let name = git_string_converter(git_reference_name(pointer.pointee)) try super.init(repository: repository, name: name, pointer: pointer) } } ================================================ FILE: FSNotesCore/Git/branch/Branches.swift ================================================ // // Branches.swift // Git2Swift // // Created by Damien Giron on 01/08/2016. // // import Foundation import Cgit2 /// Branches manager public class Branches { /// Repository public let repository: Repository /// Constructor with repository /// /// - parameter repository: Git2Swift repository /// /// - returns: Branches init(repository: Repository) { self.repository = repository } /// All branch names public func names(type: BranchType = .local) throws -> [String] { var strs = [String]() for branch in try all(type: type) { strs.append(branch.name) } return strs } /// Get branch with full reference name /// /// - parameter name: Branch name /// /// - throws: GitError (notFound, invalidSpec) /// /// - returns: Branch public func get(spec: String) throws -> Branch { let specInfo = try Branch.getSpecInfo(spec: spec) return try get(name: specInfo.name, type: specInfo.type) } /// Get branch by name /// /// - parameter name: Branch name /// - parameter type: Branch type /// /// - throws: GitError (notFound, invalidSpec) /// /// - returns: Branch public func get(name: String, type: BranchType = .local) throws -> Branch { // Find reference pointer let reference = UnsafeMutablePointer.allocate(capacity: 1) // Lookup reference let error = git_branch_lookup(reference, repository.pointer.pointee, name, git_convert_branch_type(type)) if (error != 0) { reference.deinitialize(count: 1) reference.deallocate() // 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code. switch (error) { case GIT_ENOTFOUND.rawValue : throw GitError.notFound(ref: name) case GIT_EINVALIDSPEC.rawValue: throw GitError.invalidSpec(spec: name) default: throw gitUnknownError("Unable to lookup reference \(name)", code: error) } } return try Branch(repository: repository, name:name, pointer: reference) } /// Create a new branch /// /// - parameter name: New branch name /// - parameter force: Force creation /// /// - throws: GitError /// /// - returns: Branch public func create(name: String, force: Bool = false) throws -> Branch { // Find last commit guard let lastCommit = try repository.head().targetCommit().pointer.pointee else { throw GitError.notFound(ref: "HEAD") } // new_branch let new_branch = UnsafeMutablePointer.allocate(capacity: 1) // Create branch let error = git_branch_create(new_branch, repository.pointer.pointee, name, lastCommit, force ? 1 : 0) if (error == 0) { return try Branch(repository: repository, name: name, pointer: new_branch) } else { throw gitUnknownError("Unable to create branch", code: error) } } /// Remove a branch by name and type /// /// - parameter name: Branch name /// - parameter type: Branch type /// /// - throws: GitError public func remove(name: String, type: BranchType = .local) throws { // branch let lookup_branch = UnsafeMutablePointer.allocate(capacity: 1) defer { if (lookup_branch.pointee != nil) { git_reference_free(lookup_branch.pointee) } lookup_branch.deinitialize(count: 1) lookup_branch.deallocate() } // Find type let branchType : git_branch_t if (type == .remote) { branchType = GIT_BRANCH_REMOTE } else { branchType = GIT_BRANCH_LOCAL } let error = git_branch_lookup(lookup_branch, repository.pointer.pointee, name, branchType) if (error == 0 && lookup_branch.pointee != nil) { git_branch_delete(lookup_branch.pointee) } else { throw gitUnknownError("Unable to delete branch", code: error) } } /// Find all branches /// /// - parameter type: Filter by types /// /// - throws: GitError /// /// - returns: Branch iterator public func all(type: BranchType = .local) throws -> BranchIterator { return BranchIterator(repository: repository, type: type) } } ================================================ FILE: FSNotesCore/Git/branch/BranchesIterator.swift ================================================ // // BranchesIterator.swift // Git2Swift // // Created by Damien Giron on 11/08/2016. // // import Foundation import Cgit2 /// Branch iterator public class BranchIterator : Sequence, IteratorProtocol { /// LibGit2 iterator pointer private var branch_iterator : UnsafeMutablePointer /// LibGit2 repository pointer private let repository: Repository /// Constructor with repository and type /// /// - parameter repository: Repository /// - parameter type: Type /// /// - returns: Branch iterator init(repository : Repository, type: BranchType) { self.repository = repository // Pointer of branch iterator branch_iterator = UnsafeMutablePointer.allocate(capacity: 1) // Init iterator git_branch_iterator_new(branch_iterator, repository.pointer.pointee, git_convert_branch_type(type)) } deinit { if let ptr = branch_iterator.pointee { git_branch_iterator_free(ptr) } branch_iterator.deinitialize(count: 1) branch_iterator.deallocate() } /// Find next branch /// /// - returns: Next branch or nil for the end public func next() -> Branch? { // Next branch pointer let branch = UnsafeMutablePointer.allocate(capacity: 1) // Next branch type let type = UnsafeMutablePointer.allocate(capacity: 1) defer { type.deinitialize(count: 1) type.deallocate() } // find next let result = git_branch_next(branch, type, branch_iterator.pointee) // Test next if (result != GIT_ITEROVER.rawValue) { return try? Branch(repository: repository, pointer: branch, type: git_convert_from_git_branch_t(type.pointee)) } else { // End return nil } } } ================================================ FILE: FSNotesCore/Git/commit/Commit.swift ================================================ // // Commit.swift // Git2Swift // // Created by Damien Giron on 01/08/2016. // // import Foundation import Cgit2 /// Wrap a commit public class Commit : Object { private let repository : Repository /// LibGit2 commit pointer internal let pointer : UnsafeMutablePointer /// OID public let oid: OID /// Commit summary lazy public var summary : String = { git_string_converter(git_commit_summary(self.pointer.pointee)) } () /// Commit body lazy public var body : String = { git_string_converter(git_commit_body(self.pointer.pointee)) } () /// Commit author lazy public var author : Signature = { Signature(sig: git_commit_author(self.pointer.pointee)) } () /// Commit commiter lazy public var committer : Signature = { Signature(sig: git_commit_committer(self.pointer.pointee)) } () /// Commit date lazy public var date : Date = { Date(timeIntervalSince1970: TimeInterval(git_commit_time(self.pointer.pointee))) } () /// Commit TimeZone lazy public var timeZone : TimeZone? = { TimeZone(secondsFromGMT: Int(git_commit_time_offset(self.pointer.pointee)) * 60) } () /// Constructor with libgit2 pointer and OID /// /// - parameter pointer: libgit2 commit pointer /// - parameter oid: OID /// /// - returns: Commit init(repository : Repository, pointer: UnsafeMutablePointer, oid: OID) { self.repository = repository self.pointer = pointer self.oid = oid } deinit { if let ptr = pointer.pointee { git_commit_free(ptr) } pointer.deinitialize(count: 1) pointer.deallocate() } public func tree() throws -> Tree { // Tree let tree = UnsafeMutablePointer.allocate(capacity: 1) // Find tree from commit let error = git_commit_tree(tree, pointer.pointee) if (error == 0) { return Tree(repository: repository, tree: tree) } else { throw GitError.unknownError(msg: "Unable to find tree from commit", code: error, desc: git_error_message()) } } public func getDate() -> String { date.string(format: "yyyy-MM-dd HH:mm:ss") } } ================================================ FILE: FSNotesCore/Git/commons/Blob.swift ================================================ // // Blob.swift // Git2Swift // // import Foundation import Cgit2 /// Tree Definition public class Blob { /// Internal libgit2 tree internal let blob: UnsafeMutablePointer /// Init with libgit2 tree /// /// - parameter repository: Git2Swift repository /// - parameter Blob: Libgit2 blob pointer /// /// - returns: Blob init(blob: UnsafeMutablePointer) { self.blob = blob } deinit { if let ptr = blob.pointee { git_blob_free(ptr) } blob.deinitialize(count: 1) blob.deallocate() } lazy public var rawContent: Data = { Data(bytes: git_blob_rawcontent(self.blob.pointee), count: Int(git_blob_rawsize(self.blob.pointee))) }() } ================================================ FILE: FSNotesCore/Git/commons/ConfigManager.swift ================================================ // // ConfigManager.swift // Git2Swift // // Created by Damien Giron on 01/08/2016. // // import Foundation import Cgit2 /// Config manager public class ConfigManager { /// LibGit2 config pointer private let cfg : UnsafeMutablePointer /// Constructor /// /// - throws: GitError /// /// - returns: ConfigManager init() throws { // Config cfg = UnsafeMutablePointer.allocate(capacity: 1) // Read config let error = git_config_open_default(cfg); if (error != 0) { throw gitUnknownError("Unable to open config", code: error) } } deinit { if let ptr = cfg.pointee { git_config_free(ptr) } cfg.deinitialize(count: 1) cfg.deallocate() } /// Read string /// /// - parameter key: Key value /// /// - throws: GitError wrapping libgit2 error /// /// - returns: String public func readString(key: String) throws -> String { // email entry let entry = UnsafeMutablePointer?>.allocate(capacity: 1) defer { if (entry.pointee != nil) { git_config_entry_free(entry.pointee) } entry.deinitialize(count: 1) entry.deallocate() } let value : String if git_config_get_entry(entry, cfg.pointee, key) == 0 { value = String(cString: entry.pointee!.pointee.value) } else { value = String("") } return value } } ================================================ FILE: FSNotesCore/Git/commons/Error.swift ================================================ // // Error.swift // Git2Swift // // Created by Damien Giron on 31/07/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 public enum GitError : Error { case invalidSHA(sha: String) case notFound(ref: String) case invalidSpec(spec: String) case alreadyExists(ref: String) case ambiguous(msg: String) case invalidReference(msg: String, type: ReferenceType) case unknownReference(msg: String) case unknownError(msg: String, code: Int32, desc: String) case unableToMerge(msg: String) case modifiedElsewhere(ref: String) case notImplemented(msg: String) case uncommittedConflict case noAddedFiles func associatedValue() -> String { switch self { case .invalidSHA(sha: let sha): return "Invalid sha \(sha)" case .notFound(ref: let ref): return "Not found ref \(ref)" case .invalidSpec(spec: let spec): return "Invalid spec \(spec)" case .alreadyExists(ref: let ref): return "Already exist ref \(ref)" case .ambiguous(msg: let msg): return "Ambiguous \(msg)" case .invalidReference(msg: let msg, type: let type): return "Invalid ref \(msg) \(type)" case .unknownReference(msg: let msg): return "Unknown ref \(msg)" case .unknownError(msg: let msg, code: let code, desc: let desc): return "\(msg) \(code) \(desc)" case .unableToMerge(msg: let msg): return "Unable to merge \(msg)" case .modifiedElsewhere(ref: let ref): return "Modified error \(ref)" case .notImplemented(msg: let msg): return "Not implemented \(msg)" case .uncommittedConflict: return "Uncommitted conflict" case .noAddedFiles: return "New files not found" } } } func gitUnknownError(_ msg: String, code: Int32) -> GitError { return GitError.unknownError(msg: msg, code: code, desc: git_error_message()) } /// /// Error message. /// - Returns message. /// func git_error_message() -> String { let error = giterr_last() if (error != nil) { return "\(String(cString: error!.pointee.message))" } else { return "" } } ================================================ FILE: FSNotesCore/Git/commons/Errors.swift ================================================ import Foundation import Cgit2 public let libGit2ErrorDomain = "org.libgit2.libgit2" internal extension NSError { /// Returns an NSError with an error domain and message for libgit2 errors. /// /// :param: errorCode An error code returned by a libgit2 function. /// :param: libGit2PointOfFailure The name of the libgit2 function that produced the /// error code. /// :returns: An NSError with a libgit2 error domain, code, and message. convenience init(gitError errorCode: Int32, pointOfFailure: String? = nil) { let code = Int(errorCode) var userInfo: [String: String] = [:] if let message = errorMessage(errorCode) { userInfo[NSLocalizedDescriptionKey] = message } else { userInfo[NSLocalizedDescriptionKey] = "Unknown libgit2 error." } if let pointOfFailure = pointOfFailure { userInfo[NSLocalizedFailureReasonErrorKey] = "\(pointOfFailure) failed." } self.init(domain: libGit2ErrorDomain, code: code, userInfo: userInfo) } } /// Returns the libgit2 error message for the given error code. /// /// The error message represents the last error message generated by /// libgit2 in the current thread. /// /// :param: errorCode An error code returned by a libgit2 function. /// :returns: If the error message exists either in libgit2's thread-specific registry, /// or errno has been set by the system, this function returns the /// corresponding string representation of that error. Otherwise, it returns /// nil. private func errorMessage(_ errorCode: Int32) -> String? { let last = giterr_last() if let lastErrorPointer = last { return String(validatingUTF8: lastErrorPointer.pointee.message) } else if UInt32(errorCode) == GIT_ERROR_OS.rawValue { return String(validatingUTF8: strerror(errno)) } else { return nil } } ================================================ FILE: FSNotesCore/Git/commons/OID.swift ================================================ // // OID.swift // Git2Swift // // Created by Damien Giron on 31/07/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 /// Define git OID public struct OID { /// LibGit2 OID internal var oid = git_oid() /// Init with libgit2 OID /// /// - parameter oid: Internal libgit2 /// /// - returns: OID init(withGitOid oid: git_oid) { self.oid = oid } /// Init with git SHA /// /// - parameter sha: String containing SHA /// /// - throws: GitError /// /// - returns: OID public init(withSha sha: String?) throws { guard let sha = sha else { throw GitError.invalidSHA(sha: "nil") } if git_oid_fromstr(&self.oid, sha) != 0 { throw GitError.invalidSHA(sha: sha) } } /// Retrieve SHA string /// /// - returns: String containing SHA or nil public func sha() -> String? { // Create c_string let c_string = UnsafeMutablePointer.allocate(capacity: 40) defer { c_string.deinitialize(count: 40) c_string.deallocate() } // Copy string var oid = self.oid git_oid_fmt(c_string, &oid) // Convert to string return String(bytesNoCopy: c_string, length: 40, encoding: String.Encoding.utf8, freeWhenDone: false) } } ================================================ FILE: FSNotesCore/Git/commons/Object.swift ================================================ // // Object.swift // Git2Swift // // Created by Damien Giron on 01/08/2016. // // import Foundation /// Object public protocol Object { /// Oid var oid : OID { get } } ================================================ FILE: FSNotesCore/Git/commons/Progress.swift ================================================ // // Progress.swift // Git2Swift // // Created by Dami on 24/09/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 #if os(iOS) import UIKit #else import AppKit #endif /// Define progress protocol public class GitProgress { public var project: Project #if os(iOS) public var statusTextField: UITextField init(statusTextField: UITextField, project: Project) { self.statusTextField = statusTextField self.project = project } #else public var statusTextField: NSTextField init(statusTextField: NSTextField, project: Project) { self.statusTextField = statusTextField self.project = project } #endif func log(current: Int, total: Int, action: String) { let message = "git \(action): chunk \(current) from \(total)" send(message: message) } func log(message: String) { project.gitStatus = message send(message: message) } func send(message: String) { print(message) DispatchQueue.main.async { #if os(iOS) self.statusTextField.text = message #else self.statusTextField.stringValue = message #endif } } } final class ProgressDelegate { static let checkoutIgnoreFilesProgressCallback: git_checkout_notify_cb = {a,b,c,d,e,f in // Dirty found – skip checkout, commit before if a.rawValue == 2 { return -1 } return 0 } static let fetchProgressCallback: git_transfer_progress_cb = { stats, payload in if let stats = stats { AppDelegate.gitProgress?.log(current: Int(stats.pointee.received_objects), total: Int(stats.pointee.total_objects), action: "fetch") } return 0 } static let pushProgressCallback: git_push_transfer_progress_cb = { current, total, bytes, payload in AppDelegate.gitProgress?.log(current: Int(current), total: Int(total), action: "push") return 0 } static let packBuilderCallback: git_packbuilder_progress = { stage, current, total, payload in AppDelegate.gitProgress?.log(current: Int(current), total: Int(total), action: "pack") return 0 } static let checkoutProgressCallback: git_checkout_progress_cb = { path, completed, total, payload in AppDelegate.gitProgress?.log(current: Int(completed), total: Int(total), action: "checkout") } } ================================================ FILE: FSNotesCore/Git/commons/Signature.swift ================================================ // // Signature.swift // Git2Swift // // Created by Damien Giron on 01/08/2016. // // import Foundation import Cgit2 /// Define a git signature /// /// Wrap name and email public class Signature { /// /// Signature name. /// public let name : String /// /// Signature mail. /// public let email : String /// Constructor with name and email /// /// - parameter name: Signature name /// - parameter email: Signature email /// /// - returns: Signature public init(name: String, email: String) { self.name = name self.email = email } /// Constructor with libgit2 signature pointer /// /// - parameter sig: libgit2 signature /// /// - returns: Signature convenience init(sig : UnsafePointer) { self.init(name: git_string_converter(sig.pointee.name), email: git_string_converter(sig.pointee.email)) } /// /// Create now signature. /// - Parameter sig : signature /// - Throws GitError /// /// Create libgit2 rsignature /// /// - parameter sig: Pointer where creating libgit2 signature /// /// - throws: GitError internal func now(sig : UnsafeMutablePointer?>) throws { let error = git_signature_now(sig, name, email) if (error != 0) { throw gitUnknownError("", code: error) } } } ================================================ FILE: FSNotesCore/Git/commons/StaticSshKeyDelegate.swift ================================================ // // StaticSshKeyDelegate.swift // FSNotes // // Created by Oleksandr Hlushchenko on 14.10.2022. // Copyright © 2022 Oleksandr Hlushchenko. All rights reserved. // import Foundation class StaticSshKeyDelegate : SshKeyDelegate { let privateUrl: URL let publicUrl: URL? let passphrase: String init(privateUrl: URL, passphrase: String, publicUrl: URL? = nil) { self.privateUrl = privateUrl self.publicUrl = publicUrl self.passphrase = passphrase } public func get(username: String?, url: URL?) -> SshKeyData { if let publicUrl = self.publicUrl { return RawSshKeyData(username: username, privateKey: privateUrl, publicKey: publicUrl, passphrase: passphrase) } return RawSshKeyData(username: username, privateKey: privateUrl, publicKey: nil, passphrase: passphrase) } } ================================================ FILE: FSNotesCore/Git/commons/Strings.swift ================================================ // // Strings.swift // Git2Swift // // Created by Damien Giron on 31/07/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 /// Convert libgit2 string to Swift string /// /// - parameter cStr: C string pointer /// /// - returns: Swift string func git_string_converter(_ cStr: UnsafePointer) -> String { return String(cString: cStr) } /// Convert libgit2 string array to swift string array /// /// - parameter strarray: libgit2 string array /// /// - returns: String array func git_strarray_to_strings(_ strarray: inout git_strarray) -> [String] { var strs = [String]() let count = strarray.count if (count == 0) { return strs } strs.reserveCapacity(count) for i in 0...(count - 1) { strs.append(String(cString: strarray.strings[i]!)) } return strs } /// /// Define a string wrapper used to transform a String array to pointer array. /// class StringWrapper { /// /// Raw UTF-8 /// private var rawUtf8 = [ContiguousArray]() /// /// Character count. /// let count : Int /// /// Pointer to 'const char **' /// private(set) var pointer : UnsafeMutablePointer?> /// /// Init string wrapper. /// - Parameter strs : String array. /// init(withStrs strs: [String]) { // String count count = strs.count // Create result pointer = UnsafeMutablePointer?>.allocate(capacity: count) // Store raw utf8 rawUtf8.reserveCapacity(count) // Init iterator var iterator = pointer // Iterate other strings for str in strs { // Create utf8 cString let cStr : ContiguousArray = str.utf8CString rawUtf8.append(cStr) // Set pointer let cStr1 = cStr.withUnsafeBufferPointer { UnsafeMutablePointer(mutating: $0.baseAddress) } // Initialize with utf8 data iterator.initialize(to: cStr1) // Add successor iterator = iterator.successor() } } deinit { pointer.deinitialize(count: 1) pointer.deallocate() } } ================================================ FILE: FSNotesCore/Git/commons/Wrapper.swift ================================================ // // Wrapper.swift // Git2Swift // // Created by Damien Giron on 20/09/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation /// Wrap authentication to C function internal class CWrapper { /// Object let object: T /// Init wrapper /// /// - parameter object: Wrapper /// /// - returns: Wrapper init(_ object: T) { self.object = object } } ================================================ FILE: FSNotesCore/Git/diff/Diff.swift ================================================ // // Diff.swift // Git2Swift // // Created by Damien Giron on 20/09/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 var paths = NSMutableDictionary() /// Define internal object to cast and use with C hander class InternalDiffWrapper { /// Diff object var diff : Diff /// Entries parsed var entries = Dictionary() init(_ diff: Diff) { self.diff = diff } } /// Diff public class Diff { public static var commitsDict = ["sha": [String]()] /// Internal Diff pointer internal let pointer : UnsafeMutablePointer // Init with libgit2 diff pointer init(pointer: UnsafeMutablePointer) { self.pointer = pointer } deinit { if let ptr = pointer.pointee { git_diff_free(ptr) } pointer.deinitialize(count: 1) pointer.deallocate() } /// Find diff entry by path public func find(byPath path: String, oid: OID, project: Project? = nil) -> Bool { project?.loadCommitsCache() guard let sha = oid.sha() else { return false } paths.removeAllObjects() if let project = project, let paths = project.commitsCache[sha] { return paths.contains(path) } // Create internal object to convert in C pointer let payload = InternalDiffWrapper(self) // COnvert in C pointer let ptr = Unmanaged.passRetained(CWrapper(payload)).toOpaque() // Foreach on all diff entries let error = git_diff_foreach(self.pointer.pointee, DiffFile.callback, nil, nil, nil, ptr) if (error != 0) { NSLog("git diff error \(git_error_message())") } if let sha = oid.sha(), let keys = paths.allKeys as? [String], let project = project { project.commitsCache[sha] = keys } return paths[path] != nil } } final class DiffFile{ static let callback: git_diff_file_cb = { delta, progress, payload in if let delta = delta { // Create diff entry let diffEntry = DiffEntry(delta: delta) // Test old name if let oldName = diffEntry.oldName { paths[oldName] = true } // Test new name if let newName = diffEntry.newName { paths[newName] = true } } return 0 } } ================================================ FILE: FSNotesCore/Git/diff/DiffEntry.swift ================================================ // // DiffEntry.swift // Git2Swift // // Created by Damien Giron on 21/09/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 /// Delta type /// /// - unmodified: no changes /// - added: entry does not exist in old version /// - deleted: entry does not exist in new version /// - modified: entry content changed between old and new /// - renamed: entry was renamed between old and new /// - copied: entry was copied from another old entry /// - ignored: entry is ignored item in workdir /// - untracked: entry is untracked item in workdir /// - typechange: type of entry changed between old and new /// - unreadable: entry is unreadable /// - conflicted: entry in the index is conflicted /// - unknown: Enum size public enum DeltaType : UInt32 { case unmodified = 0 case added = 1 case deleted = 2 case modified = 3 case renamed = 4 case copied = 5 case ignored = 6 case untracked = 7 case typechange = 8 case unreadable = 9 case conflicted = 10 case unknown } /// Diff entry public class DiffEntry { /// Old file name public let oldName : String? /// New file name public let newName : String? /// Delta entry type public let type : DeltaType init(delta: UnsafePointer) { let ptr = delta.pointee oldName = git_string_converter(ptr.old_file.path) newName = git_string_converter(ptr.old_file.path) if let type = DeltaType(rawValue: ptr.status.rawValue) { self.type = type } else { self.type = .unknown } } } public class DiffFileNew { init(delta: UnsafePointer) { let ptr = delta.pointee print(git_string_converter(ptr.path)) } } ================================================ FILE: FSNotesCore/Git/head/Head+Checkout.swift ================================================ // // Head+Checkout.swift // Git2Swift // // Created by Damien Giron on 07/08/2016. // // import Foundation import Cgit2 /// Define checkout type /// /// - none: default checkout /// - safe: todo /// - recreateMissing: todo /// - force: todo public enum CheckoutType { case none case safe case recreateMissing case force } /// Reset type /// /// - soft: todo /// - mixed: todo /// - hard: todo public enum ResetType { case soft case mixed case hard } // MARK: - Head extension for checkout extension Head { /// Checkout a branch and swith HEAD to this /// /// - parameter branch: branch /// - parameter progress: Progress object /// /// - throws: GitError /// - GitError.NOT_FOUND : if branch not found /// - GitError.UNKNOW_ERROR : for unknow error public func checkout(branch: Branch, progress: Progress? = nil, type: CheckoutType = .safe) throws { // Checkout new tree try checkout(tree: try branch.revTree(), type: type, progress: progress) // Set head let error = git_repository_set_head(repository.pointer.pointee, branch.name) if (error != 0) { throw gitUnknownError("Unable to switch to '\(branch.name)' branch", code: error) } } /// Checkout a tag and swith HEAD to this /// /// - parameter tag: tag /// - parameter progress: Progress object /// /// - throws: GitError /// - GitError.NOT_FOUND : if branch not found /// - GitError.UNKNOW_ERROR : for unknow error public func checkout(tag: Tag, progress: Progress? = nil) throws { // Checkout new tree try checkout(tree: try tag.revTree(), type: .safe, progress: progress) // Set repository to tag let error = git_repository_set_head(repository.pointer.pointee, tag.name) if (error != 0) { throw gitUnknownError("Unable to switch to '\(tag.name)' tag", code: error) } } /// Checkout tree to Head /// /// - parameter tree: File tree /// - parameter type: Checkout type /// - parameter progress: Progress object /// /// - throws: GitError public func checkout(tree: Tree, type: CheckoutType, progress: Progress? = nil) throws { // Select checkout strategy var opts = git_checkout_options() opts.version = 1 opts.progress_cb = ProgressDelegate.checkoutProgressCallback switch type { case .none: opts.checkout_strategy = GIT_CHECKOUT_NONE.rawValue; opts.notify_cb = ProgressDelegate.checkoutIgnoreFilesProgressCallback opts.notify_flags = GIT_CHECKOUT_NOTIFY_DIRTY.rawValue case .safe: opts.checkout_strategy = GIT_CHECKOUT_SAFE.rawValue; case .recreateMissing: opts.checkout_strategy = GIT_CHECKOUT_RECREATE_MISSING.rawValue; case .force: opts.checkout_strategy = GIT_CHECKOUT_FORCE.rawValue; } // Checkout new tree let error = git_checkout_tree(repository.pointer.pointee, tree.tree.pointee, &opts); if (error != 0) { throw gitUnknownError("Unable to checkout to tree", code: error) } } /// Reset head. /// /// - parameter type: Reset type /// - parameter progress: Progress object /// /// - throws: GitError public func reset(type: ResetType, progress: Progress? = nil) throws { // Set checkout option var opts = git_checkout_options() opts.version = 1 // Set progress opts.progress_cb = ProgressDelegate.checkoutProgressCallback //setCheckoutProgressHandler(options: &opts, progress: progress) let gType : git_reset_t switch (type) { case .soft : gType = GIT_RESET_SOFT case .mixed : gType = GIT_RESET_MIXED case .hard : gType = GIT_RESET_HARD } // Reset directory let error = git_reset(repository.pointer.pointee, try targetCommit().pointer.pointee, gType, &opts) if (error != 0) { throw gitUnknownError("Unable to reset 'HEAD'", code: error) } } } ================================================ FILE: FSNotesCore/Git/head/Head+Merge.swift ================================================ // // Head+Merge.swift // Git2Swift // // Created by Damien Giron on 08/08/2016. // // import Foundation import Cgit2 /// Merge type /// /// - none: No entries to merge /// - upToDate: All entries up to date /// - fastForward: Fast forward /// - normal: Nomral merge public enum MergeType { case none case upToDate case fastForward case normal } // MARK: - Head extension for merging extension Head { /// Head analysis /// /// - parameter branch: branch to analysis /// /// - throws: GitError /// /// - returns: MergeType résult public func analysis(branch: Branch) throws -> MergeType { // Find oid var oid = try branch.targetCommit().oid // Find annotated commit let annotatedCommit = UnsafeMutablePointer.allocate(capacity: 1) defer { if let ptr = annotatedCommit.pointee { git_annotated_commit_free(ptr) } annotatedCommit.deinitialize(count: 1) annotatedCommit.deallocate() } // Find annoted commit var error = git_annotated_commit_lookup(annotatedCommit, repository.pointer.pointee, &oid.oid) if (error != 0) { throw gitUnknownError("Analysis : unable to create annotated commit", code: error) } // Allow fast-forward or normal merge var preference : git_merge_preference_t = GIT_MERGE_PREFERENCE_NONE // Merge analysis var analysis = GIT_MERGE_ANALYSIS_NONE error = git_merge_analysis(&analysis, &preference, repository.pointer.pointee, annotatedCommit, 1) if (error != 0) { throw gitUnknownError("Unable to analysis", code: error) } /* print("GIT_MERGE_ANALYSIS_NONE : \(analysis.rawValue & GIT_MERGE_ANALYSIS_NONE.rawValue)") print("GIT_MERGE_ANALYSIS_NORMAL : \(analysis.rawValue & GIT_MERGE_ANALYSIS_NORMAL.rawValue)") print("GIT_MERGE_ANALYSIS_UP_TO_DATE : \(analysis.rawValue & GIT_MERGE_ANALYSIS_UP_TO_DATE.rawValue)") print("GIT_MERGE_ANALYSIS_FASTFORWARD : \(analysis.rawValue & GIT_MERGE_ANALYSIS_FASTFORWARD.rawValue)") print("GIT_MERGE_ANALYSIS_UNBORN : \(analysis.rawValue & GIT_MERGE_ANALYSIS_UNBORN.rawValue)") */ // Test up to date if (analysis.rawValue & GIT_MERGE_ANALYSIS_UP_TO_DATE.rawValue != 0) { return .upToDate } // Test fast-froward if (analysis.rawValue & GIT_MERGE_ANALYSIS_FASTFORWARD.rawValue != 0) { return .fastForward } // Test normal if (analysis.rawValue & GIT_MERGE_ANALYSIS_NORMAL.rawValue != 0) { return .normal } // Test none if (analysis.rawValue & GIT_MERGE_ANALYSIS_NONE.rawValue != 0) { return .none } throw GitError.notImplemented(msg: "Index iterator not implemented \(analysis.rawValue).") } /// Merge branch with signature /// /// - parameter branch: branch to merge /// - parameter signature: signature for commiter /// - parameter progress: Progress object /// /// - throws: GitError /// /// - returns: True if branch is merged or false if conflicted files public func merge(branch: Branch, signature: Signature, progress: Progress? = nil) throws -> Bool { // Analysis branch let mergeType = try analysis(branch: branch) switch (mergeType) { case .upToDate: return true case .fastForward: try fastForward(branch: branch, signature: signature, progress: progress) return true case .normal: return try normalMerge(branch: branch, signature: signature, progress: progress) case .none: throw GitError.unableToMerge(msg: "Unmergeable branch \(branch)") } } /// Internal fast forward /// /// - parameter branch: branch to merge /// - parameter signature: signature for commiter /// - parameter progress: Progress object /// /// - throws: GitError private func fastForward(branch: Branch, signature: Signature, progress: Progress? = nil) throws { do { // Dry run for detect dirty try checkout(tree: revTree(), type: .none, progress: progress) // All fine – force checkout try targetReference().updateTargetCommit(commit: try branch.targetCommit(), message: "Merge '\(branch.name)': Fast forward") try checkout(tree: revTree(), type: .force, progress: progress) } catch { throw GitError.uncommittedConflict } } /// Internal normal merge /// /// - parameter branch: branch to merge /// - parameter signature: signature for commiter /// - parameter progress: Progress object /// /// - throws: GitError /// /// - returns: True if branch is merged or false if conflicted files public func normalMerge(branch: Branch, signature: Signature, progress: Progress? = nil) throws -> Bool { // Merge index let mergeIndexPtr = UnsafeMutablePointer.allocate(capacity: 1) // Merge let tCommit = try targetCommit() let bCommit = try branch.targetCommit() let error = git_merge_commits(mergeIndexPtr, repository.pointer.pointee, tCommit.pointer.pointee, bCommit.pointer.pointee, nil) if (error != 0) { // Dealloc mergeIndexPtr.deinitialize(count: 1) mergeIndexPtr.deallocate() throw gitUnknownError("Failed to merge \(branch.name) to HEAD", code: error) } // Create index let mergeIndex = Index(repository: repository, idx: mergeIndexPtr) // Check conflicts if (mergeIndex.conflicts) { // Create annotated commit let annotatedCommit = UnsafeMutablePointer.allocate(capacity: 1) defer { if let ptr = annotatedCommit.pointee { git_annotated_commit_free(ptr) } annotatedCommit.deinitialize(count: 1) annotatedCommit.deallocate() } // Init annoted commit var oid = try branch.targetCommit().oid var error = git_annotated_commit_lookup(annotatedCommit, repository.pointer.pointee, &oid.oid) if (error != 0) { let shaCommit = oid.sha() ?? "no sha" throw gitUnknownError("Unable to annotated commit \(shaCommit)", code: error) } // Write conflicts var merge_opts = git_merge_options() merge_opts.version = 1 var checkout_opts = git_checkout_options() checkout_opts.version = 1 checkout_opts.checkout_strategy = GIT_CHECKOUT_ALLOW_CONFLICTS.rawValue // Set progress checkout_opts.progress_cb = ProgressDelegate.checkoutProgressCallback // Merge error = git_merge(repository.pointer.pointee, annotatedCommit, 1, &merge_opts, &checkout_opts) if (error != 0) { throw gitUnknownError("Unable to merge conflicted branch \(branch.name)", code: error) } do { return try resolveConflicts(annotatedCommit: annotatedCommit, signature: signature) } catch { print("Automatic conflict resolution failed") } return false } else { // Write tree to repository let tree = try repository.write(index: mergeIndex) // Commit _ = try repository.createCommit(tree: tree, parents: [try targetCommit(), try branch.targetCommit()], msg: "Merge branch '\(branch.name)'", signature: signature) // Checkout new commit try checkout(tree: try repository.head().revTree(), type: .force, progress: progress) return true } } private func conflictPaths(index: OpaquePointer) -> [String]? { var iterator: OpaquePointer? var result = git_index_conflict_iterator_new(&iterator, index) defer { git_index_conflict_iterator_free(iterator) } guard result == GIT_OK.rawValue else { return nil } var paths = [String]() var entry: UnsafePointer? var our: UnsafePointer? var their: UnsafePointer? while true { result = git_index_conflict_next(&entry, &our, &their, iterator!) if result == GIT_ITEROVER.rawValue { break } guard result == GIT_OK.rawValue else { return nil } if let entry = entry { paths.append(String(cString: entry.pointee.path)) } } return paths } private func resolveConflicts(annotatedCommit: UnsafeMutablePointer, signature: Signature) throws -> Bool { var index: OpaquePointer? = nil git_repository_index(&index, repository.pointer.pointee) guard let index = index else { return false } guard let paths = conflictPaths(index: index) else { return false } for path in paths { git_index_add_bypath(index, path) git_index_conflict_remove(index, path) } git_index_conflict_cleanup(index) git_index_write(index) var headRef: OpaquePointer? = nil git_repository_head(&headRef, repository.pointer.pointee) let rHead = try repository.head() let tCommit = try rHead.targetCommit() guard let lastCommit = tCommit.pointer.pointee else { throw GitError.notFound(ref: "HEAD") } guard let commitID = git_annotated_commit_id(annotatedCommit.pointee) else { return false } let parent2 = try repository.commitLookup(oid: OID(withGitOid: commitID.pointee)) var treeOid = git_oid() git_index_write_tree(&treeOid, index) var tree : OpaquePointer? = nil git_tree_lookup(&tree, repository.pointer.pointee, &treeOid); let sig = UnsafeMutablePointer?>.allocate(capacity: 1) defer { if let ptr = sig.pointee { git_signature_free(ptr) } sig.deinitialize(count: 1) sig.deallocate() } // Create now signature try signature.now(sig: sig) // Parents var parentsPtr : UnsafeMutablePointer? = nil defer { if let ptr = parentsPtr { ptr.deinitialize(count: 1) ptr.deallocate() } } parentsPtr = UnsafeMutablePointer.allocate(capacity: 2) var it = parentsPtr! it.initialize(to: lastCommit) it = it.successor() it.initialize(to: parent2.pointer.pointee) it = it.successor() // Create merge commit var commit_id = git_oid() let commitError = git_commit_create(&commit_id, repository.pointer.pointee, "HEAD", sig.pointee, sig.pointee, "UTF-8", "Merge conflict", tree, 2, parentsPtr ) if (commitError != 0) { throw gitUnknownError("Unable to create commit", code: commitError) } git_tree_free(tree) git_repository_state_cleanup(repository.pointer.pointee) return true } } ================================================ FILE: FSNotesCore/Git/head/Head.swift ================================================ // // Head.swift // Git2Swift // // Created by Dami on 31/07/2016. // // import Foundation /// Head repository public class Head : Reference { /// Head index /// /// - throws: GitError /// /// - returns: Git index public func index() throws -> Index { return try Index(repository: repository) } } ================================================ FILE: FSNotesCore/Git/index/Index+Commit.swift ================================================ // // Index+Commit.swift // Git2Swift // // Created by Damien Giron on 01/08/2016. // // import Foundation // MARK: - Index extension for commit extension Index { /// Create initial commit /// /// - parameter msg: Commit message /// - parameter signature: Author /// /// - throws: GitError /// /// - returns: Commit internal func createInitialCommit(msg: String, signature: Signature) throws -> Commit { return try repository.createCommit(idx: idx, parent: nil, msg: msg, signature: signature) } /// Create commit /// /// - parameter msg: Commit message /// - parameter signature: Author /// /// - throws: GitError /// /// - returns: Commit public func createCommit(msg: String, signature: Signature) throws -> Commit { return try repository.createCommit(idx: idx, parent: try repository.head().targetCommit(), msg: msg, signature: signature) } } ================================================ FILE: FSNotesCore/Git/index/Index+Files.swift ================================================ // // Index+Files.swift // Git2Swift // // Created by Damien Giron on 01/08/2016. // // import Foundation import Cgit2 /// /// Find relative path to an other url. /// func relativePath(from: URL, to: URL) -> String? { // Find current file path let currentFilePath = from.absoluteURL.path // Find from path let fromFilePath = to.path // Check if current URL is parent of from guard (currentFilePath.hasPrefix(fromFilePath)) else { return nil } // Find prefix size let size = fromFilePath.count + (currentFilePath[currentFilePath.startIndex] == "/" ? 1 : 0 ) let startIndex = currentFilePath.index(currentFilePath.startIndex, offsetBy: size) // Return sub string return String(currentFilePath[startIndex...]) } // MARK: - Index extension for files extension Index { /// Add item /// /// - parameter url: URL of the item /// /// - throws: GitError public func addItem(at url: URL) throws { // Create relative path guard let path = relativePath(from: url, to: repository.url) else { throw GitError.notFound(ref: url.absoluteString) } let error = git_index_add_bypath(idx.pointee, path); if (error != 0) { throw gitUnknownError("Unable to add item to index", code: error) } } /// Add item /// /// - parameter url: URL of the item /// /// - throws: GitError public func addItem(data: Data, at path: String) throws { var index_entry = git_index_entry() try path.withCString { (ptr: UnsafePointer) -> Void in index_entry.path = ptr index_entry.mode = 33188 /* create a blob from our buffer */ try data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> Void in let error = git_index_add_frombuffer(idx.pointee, &index_entry, bytes.baseAddress, data.count) if error != 0 { throw gitUnknownError("Unable to add Data to index", code: error) } } } } /// Remove item /// /// - parameter url: URL of the item /// /// - throws: GitError public func removeItem(at url: URL) throws { // Create relative path guard let path = relativePath(from: url, to: repository.url) else { throw GitError.notFound(ref: url.absoluteString) } let error = git_index_remove_bypath(idx.pointee, path) if (error != 0) { throw gitUnknownError("Unable to remove item to index", code: error) } } /// Save index /// /// - throws: GitError public func save() throws { let error = git_index_write(idx.pointee) if (error != 0) { throw gitUnknownError("Unable to save index", code: error) } } /// Reload index /// /// - throws: GitError public func reload() throws { let error = git_index_read(idx.pointee, 1); // A for true if (error != 0) { throw gitUnknownError("Unable to read index", code: error) } } /// Clear index /// /// - throws: GitError public func clear() throws { let error = git_index_clear(idx.pointee); if (error != 0) { throw gitUnknownError("Unable to read index", code: error) } } static var count = 0 static let gitIndexCallback: git_index_matched_path_cb = { path, match, payload in let newPath: String = git_string_converter(path!) if newPath.startsWith(string: ".Trash") { return 1 } count += 1 return 0 } public func add(path: String) -> Bool { var dirPointer = UnsafeMutablePointer(mutating: (path as NSString).utf8String) var paths = withUnsafeMutablePointer(to: &dirPointer) { git_strarray(strings: $0, count: 1) } idx.pointee.flatMap { index in defer { git_index_free(index) } let addResult = git_index_add_all(index, &paths, 0, Index.gitIndexCallback, nil) guard addResult == GIT_OK.rawValue else { print("git_index_add_all \(addResult)") return } // write index to disk let writeResult = git_index_write(index) guard writeResult == GIT_OK.rawValue else { print("git_index_write \(writeResult)") return } } let success = Index.count > 0 // reset Index.count = 0 return success } } ================================================ FILE: FSNotesCore/Git/index/Index.swift ================================================ // // Index.swift // Git2Swift // // Created by Damien Giron on 01/08/2016. // // import Foundation import Cgit2 /// Git index public class Index { /// Git2Swift repository public let repository : Repository /// Libgit2 index pointer internal let idx: UnsafeMutablePointer /// Has conflict in index public var conflicts : Bool { get { return git_index_has_conflicts(idx.pointee) == 1 } } /// Constructor with repository and libgit2 index pointer /// /// - parameter repository: Git2Swift repository /// - parameter idx: Libgit2 index /// /// - returns: Index init(repository: Repository, idx: UnsafeMutablePointer) { self.repository = repository self.idx = idx } /// Constructor with repository and return repository index /// /// - parameter repository: Git2Swift repository /// /// - throws: GitError /// /// - returns: Index convenience init(repository: Repository) throws { let idx = UnsafeMutablePointer.allocate(capacity: 1) // Create index let error = git_repository_index(idx, repository.pointer.pointee) if (error != 0) { idx.deinitialize(count: 1) idx.deallocate() throw gitUnknownError("Unable to init repository index", code: error) } self.init(repository: repository, idx: idx) } deinit { if let ptr = idx.pointee { git_index_free(ptr) } idx.deinitialize(count: 1) idx.deallocate() } } ================================================ FILE: FSNotesCore/Git/reference/Reference+Target.swift ================================================ // // Reference+Target.swift // Git2Swift // // Created by Damien Giron on 08/08/2016. // // import Foundation import Cgit2 // MARK: - Reference extension for target extension Reference { /// Target commit /// /// - throws: GitError /// /// - returns: Reference public func targetCommit() throws -> Commit { // Create commit let target = UnsafeMutablePointer.allocate(capacity: 1) // Find commit let error = git_reference_peel(target, pointer.pointee, GIT_OBJECT_COMMIT) if (error != 0) { target.deinitialize(count: 1) target.deallocate() switch error { case GIT_EAMBIGUOUS.rawValue: throw GitError.ambiguous(msg: "HEAD -> target") case GIT_ENOTFOUND.rawValue: throw GitError.notFound(ref: "HEAD") default: throw gitUnknownError("HEAD -> target", code: error) } } // Read oid let gOid = git_commit_id(target.pointee) if (gOid == nil) { throw GitError.notFound(ref: "nil") } // Set target return Commit(repository: repository, pointer: target, oid: OID(withGitOid: gOid!.pointee)) } /// Update target commit /// /// - parameter commit: New target commit /// - parameter message: log message /// /// - throws: GitError public func updateTargetCommit(commit: Commit, message: String) throws { let reference = UnsafeMutablePointer.allocate(capacity: 1) defer { if let ptr = reference.pointee { git_reference_free(ptr) } reference.deinitialize(count: 1) reference.deallocate() } var oid = commit.oid if (refType == ReferenceType.oid) { let error = git_reference_set_target(reference, self.pointer.pointee, &oid.oid, message) if (error != 0) { switch error { case GIT_EMODIFIED.rawValue: throw GitError.modifiedElsewhere(ref: name) default: throw gitUnknownError("Unable to set target", code: error) } } } else { guard let sha = oid.sha() else { throw GitError.invalidSHA(sha: "nil") } let error = git_reference_symbolic_set_target(reference, self.pointer.pointee, sha, message) if (error != 0) { let msg = "Unable to set (symbolic) target" switch error { case GIT_EINVALIDSPEC.rawValue: throw GitError.invalidSpec(spec: name) default: throw gitUnknownError(msg, code: error) } } } } } ================================================ FILE: FSNotesCore/Git/reference/Reference.swift ================================================ // // Reference.swift // Git2Swift // // Created by Dami on 31/07/2016. // // import Foundation import Cgit2 /// Refrence type /// /// - invalid: Invalid reference /// - oid: Reference OID /// - symbolic: Reference an other reference public enum ReferenceType { case invalid case oid case symbolic } /// Git2Swift reference type from libgit2 /// /// - parameter type: libgit2 type /// /// - returns: Swift2Git type func git_reference_type(_ type: git_reference_t) -> ReferenceType { switch type.rawValue { case GIT_REFERENCE_DIRECT.rawValue : return .oid case GIT_REFERENCE_SYMBOLIC.rawValue: return .symbolic default: return .invalid } } /// Fonction to retrieve tree from refrence /// /// - parameter repository: Git2Swift repository /// - parameter referenceName: Reference name /// /// - throws: GitError /// /// - returns: Git2Swift tree func git_revTree(repository: Repository, referenceName: String) throws -> Tree { // Find branch tree let revTree = UnsafeMutablePointer.allocate(capacity: 1) // Find tree let error = git_revparse_single(revTree, repository.pointer.pointee, referenceName); if (error != 0) { throw gitUnknownError("Unable to find rev-tree for '\(referenceName)'", code: error) } return Tree(repository: repository, tree: revTree) } /// Git2Swift reference public class Reference { /// Reference name public let name : String /// Git2Swift reference type public let refType : ReferenceType /// Git2Swift Repository public let repository : Repository /// Libgit2 refrence pointer internal let pointer : UnsafeMutablePointer /// Target reference /// /// - throws: GitError /// /// - returns: Swift2Git refrence public func targetReference() throws -> Reference { switch refType { case .symbolic: return try repository.referenceLookup(name: git_string_converter(git_reference_symbolic_target(pointer.pointee))) case .oid: throw GitError.invalidReference(msg: "Unable to dereference symbolic target", type: .symbolic) default: throw GitError.unknownReference(msg: "Unable to dereference symbolic target") } } /// Find rev tree /// /// - throws: GitError /// /// - returns: Tree public func revTree() throws -> Tree { return try git_revTree(repository: repository, referenceName: name) } /// Create revision walker from this reference /// /// - throws: GitError /// /// - returns: Revision walker public func revWalker() throws -> RevisionIterator { // Create walker let walker = UnsafeMutablePointer.allocate(capacity: 1) // Init walker var error = git_revwalk_new(walker, repository.pointer.pointee) guard error == 0 else { walker.deinitialize(count: 1) walker.deallocate() throw gitUnknownError("Unable to create rev walker for \(name)", code: error) } // Push reference error = git_revwalk_push_ref(walker.pointee, name) guard error == 0 else { walker.deinitialize(count: 1) walker.deallocate() throw gitUnknownError("Unable to set rev walker for \(name)", code: error) } return RevisionIterator(repository: repository, pointer: walker) } /// Constructor with repository, name and pointer /// /// - parameter repository: Git2Swift repository /// - parameter name: Reference name /// - parameter pointer: libgit2 reference pointer /// /// - throws: GitError /// /// - returns: Git2Swift reference init(repository: Repository, name: String, pointer: UnsafeMutablePointer) throws { self.repository = repository self.pointer = pointer self.name = git_string_converter(git_reference_name(pointer.pointee)) self.refType = git_reference_type(git_reference_type(pointer.pointee)) } /// Constructor with repository and pointer /// /// - parameter repository: Git2Swift repository /// - parameter pointer: libgit2 reference pointer /// /// - throws: GitError /// /// - returns: Git2Swift reference convenience init(repository: Repository, pointer: UnsafeMutablePointer) throws { // Finde name let str = git_reference_name(pointer.pointee) guard let name = str else { throw GitError.notFound(ref: "Unknown") } try self.init(repository: repository, name: git_string_converter(name), pointer: pointer) } deinit { if let ptr = pointer.pointee { git_reference_free(ptr) } pointer.deinitialize(count: 1) pointer.deallocate() } } ================================================ FILE: FSNotesCore/Git/remote/Remote.swift ================================================ // // Remote.swift // Git2Swift // // Created by Damien Giron on 17/08/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 /// Define remote repository. public class Remote { /// Repository let repository : Repository /// Remote name public let name: String /// Remote pointer internal let pointer: UnsafeMutablePointer /// Constructor with repository, pointer and name /// /// - parameter repository: repository /// - parameter pointer: pointer /// - parameter name: name /// /// - returns: Remote init(repository: Repository, pointer: UnsafeMutablePointer, name: String) { self.repository = repository self.name = name self.pointer = pointer } /// Fetch all remote branches /// - parameter authentication: Authentication callback, maybe nil /// - parameter progress: Progress object /// /// - throws: GitError public func fetch(authentication: AuthenticationHandler? = nil, progress: Progress? = nil) throws { let foPointer = UnsafeMutablePointer.allocate(capacity: 1) git_fetch_init_options(foPointer, UInt32(GIT_FETCH_OPTIONS_VERSION)) var fetchOptions = foPointer.move() foPointer.deallocate() // // // Define fetch options // var fetchOptions = git_fetch_options() fetchOptions.version = 1 fetchOptions.callbacks.version = 1 fetchOptions.prune = GIT_FETCH_PRUNE_UNSPECIFIED fetchOptions.update_fetchhead = 1 // Set progress fetchOptions.callbacks.transfer_progress = ProgressDelegate.fetchProgressCallback // test authentication if (authentication != nil) { setAuthenticationCallback(&fetchOptions.callbacks, authentication: authentication) } // Fetch remote let error = git_remote_fetch(pointer.pointee, nil, &fetchOptions, nil) if (error != 0) { throw gitUnknownError("Unable to fetch from remote", code: error) } } /// Pull all remote branches /// /// - parameter signature: Signature /// - parameter remote: Remote branch to merge /// - parameter authentication: Authentication callback, maybe nil /// - parameter progress: Progress object /// /// - throws: GitError public func pull(signature: Signature, remote: Branch? = nil, authentication: AuthenticationHandler? = nil, progress: Progress? = nil, project: Project? = nil) throws { // Fetch remote try fetch(authentication: authentication, progress: progress) let remoteBranch: Branch if (remote == nil) { // Find spec informations let specInfo = try Branch.getSpecInfo(spec: repository.head().targetReference().name) // Find remote branch remoteBranch = try repository.branches.get(name: "\(name)/\(specInfo.name)", type: .remote) } else { remoteBranch = remote! } // Merge head let head = try repository.head() _ = try head.merge(branch: remoteBranch, signature: signature, progress: progress) } /// Push a branch to remote /// /// - parameter local: Local branch /// - parameter remote: Remote branch or nil for same remote branch /// - parameter authentication: Authentication callback, maybe nil /// - parameter progress: Progress object /// /// - throws: GitError public func push(local: Branch, remote: Branch? = nil, authentication: AuthenticationHandler? = nil, progress: Progress? = nil) throws { let puPointer = UnsafeMutablePointer.allocate(capacity: 1) git_push_init_options(puPointer, UInt32(GIT_PUSH_OPTIONS_VERSION)) var pushOptions = puPointer.move() pushOptions.pb_parallelism = 8 puPointer.deallocate() // FIXME Use progress // // Set options // var opts = git_push_options() // opts.version = 1 // opts.callbacks.version = 1 // Set progress pushOptions.callbacks.push_transfer_progress = ProgressDelegate.pushProgressCallback pushOptions.callbacks.pack_progress = ProgressDelegate.packBuilderCallback // test authentication if (authentication != nil) { setAuthenticationCallback(&pushOptions.callbacks, authentication: authentication) } // Create refspec let refspec : String if (remote == nil) { refspec = "\(local.name):\(local.name)" } else { refspec = "\(local.name):refs/heads/\(remote!.shortName)" } // Create refspecs let wrapper = StringWrapper(withStrs: [refspec]) // Create str array var refspecs = git_strarray(strings: wrapper.pointer, count: wrapper.count) let error = git_remote_push(pointer.pointee, &refspecs, &pushOptions); if (error != 0) { throw gitUnknownError("Unable to push to remote", code: error) } } } ================================================ FILE: FSNotesCore/Git/remote/Remotes.swift ================================================ // // Remotes.swift // Git2Swift // // Created by Damien Giron on 17/08/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 /// Manage remote repository public class Remotes { /// Repository private let repository : Repository /// Constructor with repository manager /// /// - parameter repository: Repository /// /// - returns: Remotes init(repository: Repository) { self.repository = repository } /// Find remote names /// /// - throws: GitError /// /// - returns: String array public func remoteNames() throws -> [String] { // Store remote names var remotes = git_strarray() // List remotes let error = git_remote_list(&remotes, repository.pointer.pointee) if (error != 0) { throw gitUnknownError("Unable to list remotes", code: error) } return git_strarray_to_strings(&remotes) } /// Find remote by name. /// /// - parameter name: Remote name /// /// - throws: GitError /// /// - returns: Remote public func get(remoteName: String) throws -> Remote { // Remote pointer let remote = UnsafeMutablePointer.allocate(capacity: 1) // Lookup remote let error = git_remote_lookup(remote, repository.pointer.pointee, remoteName) if (error != 0) { remote.deinitialize(count: 1) remote.deallocate() switch(error) { case GIT_ENOTFOUND.rawValue: throw GitError.notFound(ref: remoteName) case GIT_EINVALIDSPEC.rawValue: throw GitError.invalidSpec(spec: remoteName) default: throw gitUnknownError("Unable to lookup remote \(remoteName)", code: error) } } return Remote(repository: repository, pointer: remote, name: remoteName) } /// Create remote /// /// - parameter name: Remote name /// - parameter url: URL /// /// - throws: GitError /// /// - returns: Remote public func create(name: String, url: URL) throws -> Remote { // Remote pointer let remote = UnsafeMutablePointer.allocate(capacity: 1) // Create remote let error = git_remote_create(remote, repository.pointer.pointee, name, url.path) if (error != 0) { switch(error) { case GIT_EINVALIDSPEC.rawValue: throw GitError.invalidSpec(spec: name) case GIT_EEXISTS.rawValue: throw GitError.alreadyExists(ref: name) default: throw gitUnknownError("Unable to create remote \(name)", code: error) } } return Remote(repository: repository, pointer: remote, name: name) } /// Remove remote /// /// - parameter name: Remote name /// /// - throws: GitError public func remove(name: String) throws { // remove remote let error = git_remote_delete(repository.pointer.pointee, name) if (error != 0) { throw gitUnknownError("Unable to remove remote \(name)", code: error) } } } ================================================ FILE: FSNotesCore/Git/repository/Repository+Commit.swift ================================================ // // Repository+Commit.swift // Git2Swift // // Created by Damien Giron on 11/08/2016. // // import Foundation import Cgit2 // MARK: - Repository extension for commit extension Repository { /// Internal create commit from libgit2 index /// /// - parameter idx: libgit2 index pointer /// - parameter parent: Parent commite /// - parameter msg: Message commit /// - parameter signature: Commit signature /// /// - throws: GitError /// /// - returns: Commit internal func createCommit(idx: UnsafeMutablePointer, parent: Commit?, msg: String, signature: Signature) throws -> Commit { // Create tree index var tree_id = git_oid() let error = git_index_write_tree(&tree_id, idx.pointee) if (error != 0) { throw gitUnknownError("Unable to write index to tree", code: error) } // Tree oid let oid = OID(withGitOid: tree_id) // Lookup tree let tree = try treeLookup(oid: oid) let parents : [Commit] if (parent == nil) { parents = [] } else { parents = [parent!] } return try createCommit(tree: tree, parents: parents, msg: msg, signature: signature) } /// Internal create commit with tree /// /// - parameter tree: Tree /// - parameter parents: Parent commit /// - parameter msg: Commit message /// - parameter signature: Commit signature /// /// - throws: GitError /// /// - returns: Commit internal func createCommit(tree: Tree, parents: [Commit], msg: String, signature: Signature) throws -> Commit { // Create signature let sig = UnsafeMutablePointer?>.allocate(capacity: 1) defer { if let ptr = sig.pointee { git_signature_free(ptr) } sig.deinitialize(count: 1) sig.deallocate() } // Create now signature try signature.now(sig: sig) // Find parents let parentsCount = parents.count var parentsPtr : UnsafeMutablePointer? = nil defer { if let ptr = parentsPtr { ptr.deinitialize(count: 1) ptr.deallocate() } } if (parentsCount > 0) { parentsPtr = UnsafeMutablePointer.allocate(capacity: parentsCount) var it = parentsPtr! for parent in parents { it.initialize(to: parent.pointer.pointee) it = it.successor() } } // Create empty commit var commit_id = git_oid() let error = git_commit_create(&commit_id, pointer.pointee, "HEAD", sig.pointee, sig.pointee, "UTF-8", msg, tree.tree.pointee, parentsCount, parentsPtr) if (error != 0) { throw gitUnknownError("Unable to create commit", code: error) } return try commitLookup(oid: OID(withGitOid: commit_id)) } /// Write index and return tree /// /// - parameter index: Git2Swift index /// /// - throws: GitError /// /// - returns: Tree public func write(index: Index) throws -> Tree { var gOid = git_oid() // Write tree to index let error = git_index_write_tree_to(&gOid, index.idx.pointee, pointer.pointee) if (error != 0) { throw gitUnknownError("Unable to write index to repository", code: error) } return try treeLookup(oid: OID(withGitOid: gOid)) } } ================================================ FILE: FSNotesCore/Git/repository/Repository+Lookup.swift ================================================ // // Repository+Lookup.swift // Git2Swift // // Created by Dami on 31/07/2016. // // import Foundation import Cgit2 /// Git reference lookup /// /// - parameter repository: Libgit2 repository pointer /// - parameter name: Reference name /// /// - throws: GitError /// /// - returns: Libgit2 reference pointer internal func gitReferenceLookup(repository: UnsafeMutablePointer, name: String) throws -> UnsafeMutablePointer { // Find reference pointer let reference = UnsafeMutablePointer.allocate(capacity: 1) // Lookup reference let error = git_reference_lookup(reference, repository.pointee, name) if (error != 0) { reference.deinitialize(count: 1) reference.deallocate() // 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code. switch (error) { case GIT_ENOTFOUND.rawValue : throw GitError.notFound(ref: name) case GIT_EINVALIDSPEC.rawValue: throw GitError.invalidSpec(spec: name) default: throw gitUnknownError("Unable to lookup reference \(name)", code: error) } } return reference } // MARK: - Repository extension for lookup extension Repository { /// Lookup reference /// /// - parameter name: Refrence name /// /// - throws: GitError /// /// - returns: Refernce public func referenceLookup(name: String) throws -> Reference { return try Reference(repository: self, name: name, pointer: try gitReferenceLookup(repository: pointer, name: name)) } /// Lookup a tree /// /// - parameter tree_id: Tree OID /// /// - throws: GitError /// /// - returns: Tree public func treeLookup(oid tree_id: OID) throws -> Tree { // Create tree let tree : UnsafeMutablePointer = UnsafeMutablePointer.allocate(capacity: 1) var oid = tree_id.oid let error = git_tree_lookup(tree, pointer.pointee, &oid) if (error != 0) { tree.deinitialize(count: 1) tree.deallocate() throw gitUnknownError("Unable to lookup tree", code: error) } return Tree(repository: self, tree: tree) } /// Lookup a commit /// /// - parameter commit_id: OID /// /// - throws: GitError /// /// - returns: Commit public func commitLookup(oid commit_id: OID) throws -> Commit { // Create tree let commit : UnsafeMutablePointer = UnsafeMutablePointer.allocate(capacity: 1) var oid = commit_id.oid let error = git_commit_lookup(commit, pointer.pointee, &oid) if (error != 0) { commit.deinitialize(count: 1) commit.deallocate() throw gitUnknownError("Unable to lookup commit", code: error) } return Commit(repository: self, pointer: commit, oid: OID(withGitOid: oid)) } /// Lookup a blob /// /// - parameter blob_id: OID /// /// - throws: GitError /// /// - returns: Blob public func blobLookup(oid blob_id: OID) throws -> Blob { // Create tree let blob : UnsafeMutablePointer = UnsafeMutablePointer.allocate(capacity: 1) var oid = blob_id.oid let error = git_blob_lookup(blob, pointer.pointee, &oid) if error != 0 { blob.deinitialize(count: 1) blob.deallocate() throw gitUnknownError("Unable to lookup blob", code: error) } return Blob(blob: blob) } } ================================================ FILE: FSNotesCore/Git/repository/Repository+Open.swift ================================================ // // Repository+Init.swift // Git2Swift // // Created by Damien Giron on 31/07/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 // MARK: - Repository extension for openning extension Repository { /// Constructor with URL and manager /// /// - parameter url: Repository URL /// - parameter manager: Repository manager /// /// - throws: GitError /// /// - returns: Repository convenience init(openAt url: URL, manager: RepositoryManager) throws { // Repository pointer let repository = UnsafeMutablePointer.allocate(capacity: 1) // Init repo let error = git_repository_open(repository, url.path) if (error != 0) { repository.deinitialize(count: 1) repository.deallocate() throw gitUnknownError("Unable to open repository, url: \(url)", code: error) } self.init(at: url, manager: manager, repository: repository) } /// Init new repository at URL /// /// - parameter url: Repository URL /// - parameter manager: Repository manager /// - parameter signature: Initial commiter /// - parameter bare: Create bare repository /// - parameter shared: Share repository from users /// /// - throws: GitError /// /// - returns: Repository convenience init(initAt url: URL, manager: RepositoryManager, signature: Signature, bare: Bool, shared : Bool) throws { // Repository pointer let repository = UnsafeMutablePointer.allocate(capacity: 1) // Options var options = git_repository_init_options() options.version = 1 if bare { // Set bare options.flags = GIT_REPOSITORY_INIT_BARE.rawValue } if shared { // Used shared options.mode = GIT_REPOSITORY_INIT_SHARED_ALL.rawValue } // Init repo let error = git_repository_init_ext(repository, url.path, &options) //let error = git_repository_init(repository, url.path, bare ? 1 : 0) if (error != 0) { repository.deinitialize(count: 1) repository.deallocate() throw gitUnknownError("Unable to init repository, url: \(url) (bare \(bare))", code: error) } self.init(at: url, manager: manager, repository: repository) } /// Clone a repository at URL /// /// - parameter url: URL to remote git /// - parameter at: URL to local respository /// - parameter manager: Repository manager /// - parameter authentication: Authentication /// - parameter progress: Object containing progress callbacks /// /// - throws: GitError wrapping libgit2 error /// /// - returns: Repository convenience init(cloneFrom url: URL, at: URL, manager: RepositoryManager, authentication: AuthenticationHandler? = nil, progress: Progress? = nil) throws { // Repository pointer let repository = UnsafeMutablePointer.allocate(capacity: 1) var opts = git_clone_options() opts.version = 1 // General checkouts opts.checkout_opts.version = 1 opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE.rawValue opts.checkout_opts.progress_cb = ProgressDelegate.checkoutProgressCallback // General fetchs opts.fetch_opts.version = 1 opts.fetch_opts.prune = GIT_FETCH_PRUNE_UNSPECIFIED opts.fetch_opts.update_fetchhead = 1 opts.fetch_opts.callbacks.version = 1 opts.fetch_opts.proxy_opts.version = 1 // Set fetch progress opts.fetch_opts.callbacks.transfer_progress = ProgressDelegate.fetchProgressCallback // Check handler if (authentication != nil) { setAuthenticationCallback(&opts.fetch_opts.callbacks, authentication: authentication!) } // Clone repository let error = git_clone(repository, url.absoluteString, at.path, &opts) if (error != 0) { repository.deinitialize(count: 1) repository.deallocate() throw gitUnknownError("Unable to clone repository, from \(url) to: \(at)", code: error) } self.init(at: at, manager: manager, repository: repository) } } ================================================ FILE: FSNotesCore/Git/repository/Repository.swift ================================================ // // Repository.swift // Git2Swift // // Created by Damien Giron on 31/07/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 /// Repository wrapping a libgit2 repository public class Repository { /// Repository URL public let url : URL /// Repository manager private let manager : RepositoryManager /// Libgit2 pointer to repository internal let pointer : UnsafeMutablePointer /// Branches manager lazy public private(set) var branches : Branches = { Branches(repository: self) } () /// Statuses manager lazy public private(set) var statuses : Statuses = { Statuses(repository: self) } () /// Access tags manager lazy public private(set) var tags : Tags = { Tags(repository: self) } () lazy public private(set) var remotes : Remotes = { Remotes(repository: self) } () /// Constructor with repository manager and libgit2 repository /// /// - parameter url: URL repository /// - parameter manager: Repository manager /// - parameter repository: Libgit2 repository /// /// - returns: Repository init(at url: URL, manager: RepositoryManager, repository: UnsafeMutablePointer) { self.url = url self.manager = manager self.pointer = repository } deinit { if let ptr = pointer.pointee { git_repository_free(ptr) } pointer.deinitialize(count: 1) pointer.deallocate() } /// Retrieve head /// /// - throws: GitError /// /// - returns: Head public func head() throws -> Head { // Create head return try Head(repository: self, name: "HEAD", pointer: try gitReferenceLookup(repository: pointer, name: "HEAD")) } /// Get the index for the repo. The caller is responsible for freeing the index. func unsafeIndex() -> Result { guard let ptr = pointer.pointee else { return .failure(NSError()) } var index: OpaquePointer? = nil let result = git_repository_index(&index, ptr) guard result == GIT_OK.rawValue && index != nil else { let err = NSError(gitError: result, pointOfFailure: "git_repository_index") return .failure(err) } return .success(index!) } public func add(path: String) -> Result<(), NSError> { guard pointer.pointee != nil else { return .failure(NSError()) } var dirPointer = UnsafeMutablePointer(mutating: (path as NSString).utf8String) var paths = withUnsafeMutablePointer(to: &dirPointer) { git_strarray(strings: $0, count: 1) } return unsafeIndex().flatMap { index in defer { git_index_free(index) } let addResult = git_index_add_all(index, &paths, 0, nil, nil) guard addResult == GIT_OK.rawValue else { return .failure(NSError(gitError: addResult, pointOfFailure: "git_index_add_all")) } // write index to disk let writeResult = git_index_write(index) guard writeResult == GIT_OK.rawValue else { return .failure(NSError(gitError: writeResult, pointOfFailure: "git_index_write")) } return .success(()) } } public func addRemoteOrigin(path: String) { let result = git_remote_set_url(self.pointer.pointee, "origin", path) if result != GIT_OK.rawValue { print("Remote origin error") } } public func setWorkTree(path: String) { var configPointer: OpaquePointer? = nil var result = git_repository_config(&configPointer, self.pointer.pointee); if result != GIT_OK.rawValue { print("Config opening error") } result = git_config_set_string(configPointer, "core.worktree", path); if result != GIT_OK.rawValue { print("Core config error") } } public func checkout(commit: Commit, path: String) throws { var dirPointer = UnsafeMutablePointer(mutating: (path as NSString).utf8String) let paths = withUnsafeMutablePointer(to: &dirPointer) { git_strarray(strings: $0, count: 1) } var opts = git_checkout_options() opts.version = 1 opts.paths = paths opts.checkout_strategy = GIT_CHECKOUT_FORCE.rawValue // Checkout new tree let error = git_checkout_tree(self.pointer.pointee, commit.pointer.pointee, &opts); if (error != 0) { throw gitUnknownError("Unable to checkout commit path", code: error) } } } ================================================ FILE: FSNotesCore/Git/repository/RepositoryManager.swift ================================================ // // RepositoryManager.swift // Git2Swift // // Created by Damien Giron on 31/07/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 /// Repository manager /// /// Use to init and clear libgit2 public class RepositoryManager { /// Repository instance count private static var count : Int = 0 /// Lock manager /// /// Used to lock manager when updating repository counter private static var lock = NSLock() /// Find config manager /// /// - throws: GitError /// /// - returns: ConfigManager public func configManager() throws -> ConfigManager { return try ConfigManager() } /// Constructor /// /// If there is the first contruction, init libgit2 librairy public init() { // Lock RepositoryManager.lock.lock() // Test init lib if (RepositoryManager.count == 0) { // Init git git_libgit2_init() } RepositoryManager.count += 1 // Lock RepositoryManager.lock.unlock() } /// If there are only one repository, free libgit2 library deinit { // Lock RepositoryManager.lock.lock() RepositoryManager.count -= 1 if (RepositoryManager.count == 0) { // Shutdown git git_libgit2_shutdown() } // Lock RepositoryManager.lock.unlock() } /// Init new repository at URL /// /// - parameter url: Repository URL /// - parameter signature: Init signature /// - parameter bare: Create a bare repository /// - parameter shared: Share repository from users /// /// - throws: GitError /// /// - returns: Repository public func initRepository(at url: URL, signature: Signature, bare: Bool = false, shared : Bool = false) throws -> Repository { // Create repository let repository = try Repository(initAt: url, manager: self, signature: signature, bare: bare, shared: shared) // Create initial commit _ = try repository.head().index().createInitialCommit(msg: "Initial commit", signature: signature) return repository } /// Open repository at URL /// /// - parameter url: Repository URL /// /// - throws: GitError /// /// - returns: Repository public func openRepository(at url: URL) throws -> Repository { // Open repository return try Repository(openAt: url, manager: self) } /// Clone repository /// /// - parameter remoteUrl: Remote URL /// - parameter url: Repository URL /// - parameter progress: Progress object /// - parameter authentication: Authentication handler /// /// - throws: GitError wrapping libgit2 error /// /// - returns: Repository public func cloneRepository(from remoteUrl: URL, at url: URL, progress: Progress? = nil, authentication: AuthenticationHandler? = nil) throws -> Repository { // Open repository return try Repository(cloneFrom: remoteUrl, at: url, manager: self, authentication: authentication, progress: progress) } /// Find system signature /// /// - throws: GitError /// /// - returns: Signature public func systemSignature() throws -> Signature { let configManager = try self.configManager() let systemName = try configManager.readString(key: "user.name") let systemEmail = try configManager.readString(key: "user.email") let name : String if systemName.isEmpty { name = "Unknown" } else { name = systemName } let email : String if systemEmail.isEmpty { email = "unknown@unknown.fr" } else { email = systemEmail } return Signature(name: name, email: email) } } ================================================ FILE: FSNotesCore/Git/revision/FileHistoryIterator.swift ================================================ // // FileRevLog.swift // Git2Swift // // Created by Damien Giron on 14/09/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 /// Iterate to file history public class FileHistoryIterator: RevisionIterator { // File path private let path: String private let project: Project? // Previous commit oid private var previousOid: OID? = nil private var lastFetchedOid: OID? = nil public init(repository: Repository, path: String, refspec: String = "HEAD", project: Project? = nil) throws { self.project = project // Set path self.path = path // Create walker let walker = UnsafeMutablePointer.allocate(capacity: 1) // Init walker var error = git_revwalk_new(walker, repository.pointer.pointee) guard error == 0 else { walker.deinitialize(count: 1) walker.deallocate() throw gitUnknownError("Unable to create rev walker for '\(refspec)'", code: error) } // Push reference error = git_revwalk_push_ref(walker.pointee, refspec) guard error == 0 else { walker.deinitialize(count: 1) walker.deallocate() throw gitUnknownError("Unable to set rev walker for '\(refspec)'", code: error) } super.init(repository: repository, pointer: walker) } /// Next value /// /// - returns: Next value or nil public override func next() -> OID? { guard let oid = super.next() else { return nil } lastFetchedOid = oid do { // Find commit let currentCommit = try repository.commitLookup(oid: oid) // Find parent entry let tree = try currentCommit.tree() // Find current entry let entry = try tree.entry(byPath: path) if (entry == nil) { return diffPrev(tree: tree, oid: oid) } // Test previous if (previousOid == nil) { previousOid = oid return next() } else { return diffPrev(tree: tree, oid: oid) } } catch { NSLog("Unable to find next OID \(error)") } return nil } public func walkCacheDiff() { var gitOid = git_oid() var oids = [OID]() while git_revwalk_next(&gitOid, pointer.pointee) == 0 { let oid = OID(withGitOid: gitOid) oids.append(oid) } for oid in oids { do { guard let pOid = previousOid else { previousOid = oid continue } let currentCommit = try repository.commitLookup(oid: oid) let tree = try currentCommit.tree() let previousCommit = try repository.commitLookup(oid: pOid) let previousTree = try previousCommit.tree() let diff = try previousTree.diff(other: tree) _ = diff.find(byPath: path, oid: oid, project: project) } catch {/*_*/} previousOid = oid } } private func diffPrev(tree: Tree, oid: OID) -> OID? { guard let pOid = previousOid else { return next() } do { // Find commit let previousCommit = try repository.commitLookup(oid: pOid) // Find parent entry let previousTree = try previousCommit.tree() // Find diff let diff = try previousTree.diff(other: tree) // Find if !diff.find(byPath: path, oid: oid, project: project) { // Set previous and find next previousOid = oid return next() } else { // Save previousOid let validOid = previousOid // Set previousOid previousOid = oid return validOid; } } catch { return nil } } public func getLast() -> OID? { return lastFetchedOid } public func checkFirstCommit() -> Bool { guard let oid = lastFetchedOid else { return false } do { let currentCommit = try repository.commitLookup(oid: oid) let tree = try currentCommit.tree() let entry = try tree.entry(byPath: path) if entry != nil { return true } } catch {/*_*/} return false } public func walk() -> [OID] { var gitOid = git_oid() var oids = [OID]() var oidsValid = [OID]() while git_revwalk_next(&gitOid, pointer.pointee) == 0 { let oid = OID(withGitOid: gitOid) oids.append(oid) } for oid in oids { if let oid = getMatchedOid(oid: oid) { oidsValid.append(oid) } } return oidsValid } public func getMatchedOid(oid: OID) -> OID? { lastFetchedOid = oid do { // Find commit let currentCommit = try repository.commitLookup(oid: oid) // Find parent entry let tree = try currentCommit.tree() // Find current entry let entry = try tree.entry(byPath: path) if (entry == nil) { return diff(tree: tree, oid: oid) } // Test previous if (previousOid == nil) { previousOid = oid return nil } else { return diff(tree: tree, oid: oid) } } catch { NSLog("Unable to find next OID \(error)") } return nil } private func diff(tree: Tree, oid: OID) -> OID? { guard let pOid = previousOid else { return nil } do { // Find commit let previousCommit = try repository.commitLookup(oid: pOid) // Find parent entry let previousTree = try previousCommit.tree() // Find diff let diff = try previousTree.diff(other: tree) // Find if !diff.find(byPath: path, oid: oid, project: project) { // Set previous and find next previousOid = oid return nil } else { // Save previousOid let validOid = previousOid // Set previousOid previousOid = oid return validOid; } } catch { return nil } } } ================================================ FILE: FSNotesCore/Git/revision/RevisionIterator.swift ================================================ // // RevisionIterator.swift // Git2Swift // // Created by Damien Giron on 14/09/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 /// Revision iterator /// /// Iterate over commits /// public class RevisionIterator : Sequence, IteratorProtocol { /// Repository let repository: Repository /// Libgit2 pointer internal let pointer: UnsafeMutablePointer init(repository: Repository, pointer: UnsafeMutablePointer) { self.repository = repository self.pointer = pointer } deinit { if let ptr = pointer.pointee { git_revwalk_free(ptr) } pointer.deinitialize(count: 1) pointer.deallocate() } /// Find next oid /// /// - returns: Next branch or nil for the end public func next() -> OID? { var gitOid = git_oid() if git_revwalk_next(&gitOid, pointer.pointee) == 0 { return OID(withGitOid: gitOid) } else { return nil } } } ================================================ FILE: FSNotesCore/Git/status/Status.swift ================================================ // // Status.swift // Git2Swift // // Created by Damien Giron on 12/08/2016. // // import Foundation import Cgit2 /// Define status types /// /// - current: Todo /// - indexNew: New file in index /// - indexModified: Modified file in index /// - indexDeleted: Deleted file in index /// - indexRenamed: Renamed file in index /// - indexTypeChange: Todo /// - wtNew: New file in working directory /// - wtModified: Modified file in working directory /// - wtDeleted: Deleted file in working directory /// - wtTypeChange: Todo /// - wtRenamed: Renamed file in working directory /// - wtUnreadable: Unreadable file in working directory /// - ignored: Ignored file /// - conflicted: Conflicted file public enum StatusType : UInt32 { case current = 0 case indexNew = 1 case indexModified = 2 case indexDeleted = 4 case indexRenamed = 8 case indexTypeChange = 16 case wtNew = 128 case wtModified = 256 case wtDeleted = 512 // TODO: Unknown status case wtDeletedU = 513 case wtTypeChange = 1024 case wtRenamed = 2048 case wtUnreadable = 4096 case ignored = 16384 case conflicted = 32768 } /// Define a file status public class Status { /// File path public let path : String /// Status type public let type : StatusType /// Init with libgit2 status entry /// /// - parameter entry: Libgit2 status entry pointer /// /// - throws: GitError with libgit2 error /// /// - returns: Status init(entry: UnsafePointer) throws { // Test index if (entry.pointee.index_to_workdir != nil) { path = String(cString: entry.pointee.index_to_workdir.pointee.new_file.path!) } else if (entry.pointee.head_to_index != nil) { path = String(cString: entry.pointee.head_to_index.pointee.new_file.path!) } else { path = "" } guard let type = StatusType(rawValue: entry.pointee.status.rawValue) else { throw GitError.unknownError(msg: "Unable to init status", code: -1, desc: "Unable to find status \(entry.pointee.status)") } // Set status self.type = type } } ================================================ FILE: FSNotesCore/Git/status/StatusIterator.swift ================================================ // // StatusIterator.swift // Git2Swift // // Created by Damien Giron on 12/08/2016. // // import Foundation import Cgit2 /// Statuses iterator public class StatusIterator : Sequence, IteratorProtocol { /// Statuses libgit2 pointer private let statuses : UnsafeMutablePointer /// /// Statues count. /// private let count : Int /// Statuses count private var index = 0 /// Costructor with options /// /// - parameter repository: Git2Swift repository /// - parameter opt: libgit2 status options /// /// - throws: GitError wrapping libgit2 errors /// /// - returns: StatuesIterator init(repository : Repository, opt: inout git_status_options) throws { // Init status list statuses = UnsafeMutablePointer.allocate(capacity: 1) let error = git_status_list_new(statuses, repository.pointer.pointee, &opt) if (error != 0) { throw gitUnknownError("Unable to list status", code: error) } // List count count = git_status_list_entrycount(statuses.pointee); } /// Free iterator list deinit { if let ptr = statuses.pointee { git_status_list_free(ptr) } statuses.deinitialize(count: 1) statuses.deallocate() } /// Next value or nil public func next() -> Status? { if (index < count) { // Find status at index. let entry = git_status_byindex(statuses.pointee, index); // Inc index index += 1 if entry == nil { return nil } do { let result = try Status(entry: entry!) return result } catch { print(error.localizedDescription) return nil } } else { return nil } } } ================================================ FILE: FSNotesCore/Git/status/Statuses.swift ================================================ // // Statuses.swift // Git2Swift // // Created by Damien Giron on 12/08/2016. // // import Foundation import Cgit2 /// Manage current repository statuses public class Statuses { /// Git2Swift repository public let repository : Repository /// Clean working directory property : true if clean, false in other cases public var workingDirectoryClean: Bool { get { do { return try all().next() == nil } catch { print(error.localizedDescription) return false } } } /// Constructor with Git2Swift repository /// /// - parameter repository: Repository /// /// - returns: Statuses init(repository: Repository) { self.repository = repository } /// Return all statuses in an iterator /// /// - throws: GitError wrapping libgit2 error /// /// - returns: Statuses iterator public func all() throws -> StatusIterator { var opt = git_status_options() opt.version = 1 // Use #define 1 // Set defaults flags opt.flags = (GIT_STATUS_OPT_INCLUDE_IGNORED.rawValue | GIT_STATUS_OPT_INCLUDE_UNTRACKED.rawValue | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS.rawValue) return try StatusIterator(repository: repository, opt: &opt) } } ================================================ FILE: FSNotesCore/Git/tag/Tag.swift ================================================ // // Tag.swift // Git2Swift // // Created by Damien Giron on 12/08/2016. // // import Foundation /// Git tag public class Tag : Object { /// Git2Swift repository public let repository : Repository /// Tag name, full path like "refs/tags/sample" public let name: String /// Short name like "sample" public let shortName: String /// Targeted OID public let oid: OID /// Constructor with name and OID /// /// - parameter repository: Git2Swift repository /// - parameter name: tag name /// - parameter oid: target OID /// /// - returns: Tag init(repository: Repository, name: String, oid: OID) { self.repository = repository self.name = "refs/tags/\(name)" self.shortName = name self.oid = oid } /// Rev tree /// /// - throws: GitError /// /// - returns: Tree public func revTree() throws -> Tree { return try git_revTree(repository: repository, referenceName: name) } } ================================================ FILE: FSNotesCore/Git/tag/TagIterator.swift ================================================ // // TagIterator.swift // Git2Swift // // Created by Damien Giron on 12/08/2016. // // import Foundation import Cgit2 /// Tag iterator public class TagIterator { /// Current array index private var index = 0 /// Git2Swift repository private let repository : Repository /// Arrays of tags pointer private var tagsArray : UnsafeMutablePointer /// Constructor with Git2Swift and libgit2 array pointer /// /// - parameter repository: Git2Swift repository /// - parameter tagsArray: Libgit2 array pointer /// /// - returns: Tag iterator init(repository: Repository, tagsArray: UnsafeMutablePointer) { self.repository = repository self.tagsArray = tagsArray } deinit { git_strarray_free(tagsArray) tagsArray.deinitialize(count: 1) tagsArray.deallocate() } /// Find next tag /// /// - returns: Next tag or nil public func next() -> Tag? { if (index < tagsArray.pointee.count) { let tag = try? repository.tags.get(name: String(cString: tagsArray.pointee.strings[index]!)) if (tag == nil) { return nil } index += 1 return tag } else { return nil } } } ================================================ FILE: FSNotesCore/Git/tag/Tags.swift ================================================ // // Tags.swift // Git2Swift // // Created by Damien Giron on 12/08/2016. // // import Foundation import Cgit2 // Manage tags public class Tags { /// Git2Swift Repository public let repository: Repository /// Constructor with repository /// /// - parameter repository: Git2Swift repository /// /// - returns: Tags init(repository: Repository) { self.repository = repository } /// All tag names public func names() -> [String] { // Git array var tags = git_strarray() // List all tags git_tag_list(&tags, repository.pointer.pointee); // Convert to swift let strs = git_strarray_to_strings(&tags) // free array git_strarray_free(&tags) return strs } /// Get name /// /// - parameter name: tag name to search /// /// - throws: GitError /// /// - returns: Tag public func get(name: String) throws -> Tag { let spec : String let shortName : String // Find spec if (name.hasPrefix("refs/tags/")) { spec = name let startIndex = name.index(name.startIndex, offsetBy: 10) shortName = String(name[startIndex...]) } else { spec = "refs/tags/\(name)" shortName = name } let reference = UnsafeMutablePointer.allocate(capacity: 1) // Find tag reference let error = git_reference_lookup(reference, repository.pointer.pointee, spec) if (error != 0) { // 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code. switch error { case GIT_EINVALIDSPEC.rawValue: throw GitError.invalidSpec(spec: name) case GIT_ENOTFOUND.rawValue: throw GitError.notFound(ref: name) default: throw gitUnknownError("Unable to find tag", code: error) } } // Find git oid let gOid = git_tag_target_id(reference.pointee) if (gOid == nil) { throw GitError.notFound(ref: "refs/tags/\(name)") } return Tag(repository: repository, name: shortName, oid: OID(withGitOid: gOid!.pointee)) } /// Create new tag /// /// - parameter name: tag name /// - parameter force: force creation /// /// - throws: GitError /// /// - returns: new Tag public func create(name: String, force: Bool = false) throws -> Tag { // Tag oid var tag_oid = git_oid() // Create tag let error = git_tag_create_lightweight(&tag_oid, repository.pointer.pointee, name, try repository.head().targetCommit().pointer.pointee, force ? 1 : 0); if (error != 0) { switch error { case GIT_EEXISTS.rawValue : throw GitError.alreadyExists(ref: "refs/tags/\(name)") default: throw gitUnknownError("Unable to create tag", code: error) } } return Tag(repository: repository, name: name, oid: OID(withGitOid: tag_oid)) } /// Remove tag by name /// /// - parameter name: tag name /// /// - throws: GitError public func remove(name: String) throws { let error = git_tag_delete(repository.pointer.pointee, name) if (error != 0) { throw gitUnknownError("Unable to delete tag", code: error) } } /// Find all tag in iterator /// /// - throws: GitError /// /// - returns: Tag iterator public func all() throws -> TagIterator { return try find() } /// Find tags /// /// - parameter pattern: tag pattern /// /// - throws: GitError /// /// - returns: TagIterator public func find(withPattern pattern: String? = nil) throws -> TagIterator { // Git array let tags = UnsafeMutablePointer.allocate(capacity: 1) if let pattern = pattern { // List tags from pattern git_tag_list_match(tags, pattern, repository.pointer.pointee) } else { // List all tags git_tag_list(tags, repository.pointer.pointee); } // Return iterator form array return TagIterator(repository: repository, tagsArray: tags) } } ================================================ FILE: FSNotesCore/Git/tree/Tree.swift ================================================ // // Tree.swift // Git2Swift // // Created by Damien Giron on 01/08/2016. // // import Foundation import Cgit2 /// Tree Definition public class Tree { let repository : Repository /// Internal libgit2 tree internal let tree : UnsafeMutablePointer /// Init with libgit2 tree /// /// - parameter repository: Git2Swift repository /// - parameter tree: Libgit2 tree pointer /// /// - returns: Tree init(repository: Repository, tree : UnsafeMutablePointer) { self.tree = tree self.repository = repository } deinit { if let ptr = tree.pointee { git_tree_free(ptr) } tree.deinitialize(count: 1) tree.deallocate() } /// Find entry by path. /// /// - parameter byPath: Path of file /// /// - throws: GitError /// /// - returns: TreeEntry or nil public func entry(byPath: String) throws -> TreeEntry? { // Entry let treeEntry = UnsafeMutablePointer.allocate(capacity: 1) // Find tree entry let error = git_tree_entry_bypath(treeEntry, tree.pointee, byPath) switch error { case 0: return TreeEntry(pointer: treeEntry) case GIT_ENOTFOUND.rawValue: return nil default: throw GitError.unknownError(msg: "", code: error, desc: git_error_message()) } } /// Diff /// /// - parameter other: Other tree /// /// - returns: Diff public func diff(other: Tree) throws -> Diff { // Create diff let diff = UnsafeMutablePointer.allocate(capacity: 1) // Create diff let error = git_diff_tree_to_tree(diff, repository.pointer.pointee, tree.pointee, other.tree.pointee, nil) if (error == 0) { return Diff(pointer: diff) } else { throw GitError.unknownError(msg: "diff", code: error, desc: git_error_message()) } } } ================================================ FILE: FSNotesCore/Git/tree/TreeEntry.swift ================================================ // // TreeEntry.swift // Git2Swift // // Created by Damien Giron on 20/09/2016. // Copyright © 2016 Creabox. All rights reserved. // import Foundation import Cgit2 /// Tree entry public class TreeEntry { /// The filename of a tree entry public let name : String /// Libgit2 pointer internal let pointer : UnsafeMutablePointer init(pointer: UnsafeMutablePointer) { self.pointer = pointer self.name = git_string_converter(git_tree_entry_name(pointer.pointee)) } deinit { if let ptr = pointer.pointee { git_tree_entry_free(ptr) } pointer.deinitialize(count: 1) pointer.deallocate() } } ================================================ FILE: FSNotesCore/HtmlExtractor.swift ================================================ // // HtmlExtractor.swift // FSNotes // // Created by Oleksandr Hlushchenko on 24.08.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // import Foundation public func extractTitle(from htmlString: String) -> String? { let lowercasedHTML = htmlString.lowercased() guard let titleStartRange = lowercasedHTML.range(of: "") else { return nil } let titleContentStart = closingBracketRange.upperBound let remainingHTML = htmlString[titleContentStart...] guard let endTitleRange = remainingHTML.range(of: "", options: .caseInsensitive) else { return nil } let titleContent = String(remainingHTML[.. String { var cleaned = string let htmlEntities: [String: String] = [ "&": "&", "<": "<", ">": ">", """: "\"", "'": "'", " ": " " ] for (entity, replacement) in htmlEntities { cleaned = cleaned.replacingOccurrences(of: entity, with: replacement, options: .caseInsensitive) } let numericEntityPattern = "&#x?([0-9a-fA-F]+);" if let regex = try? NSRegularExpression(pattern: numericEntityPattern) { let range = NSRange(location: 0, length: cleaned.count) let matches = regex.matches(in: cleaned, range: range) for match in matches.reversed() { if let numberRange = Range(match.range(at: 1), in: cleaned) { let numberString = String(cleaned[numberRange]) let isHex = cleaned[match.range].contains("&#x") var characterCode: Int? if isHex { characterCode = Int(numberString, radix: 16) } else { characterCode = Int(numberString) } if let code = characterCode, let unicodeScalar = UnicodeScalar(code) { let character = String(Character(unicodeScalar)) if let fullRange = Range(match.range, in: cleaned) { cleaned = cleaned.replacingCharacters(in: fullRange, with: character) } } } } } return cleaned .trimmingCharacters(in: .whitespacesAndNewlines) .replacingOccurrences(of: "\\s+", with: " ", options: .regularExpression) } ================================================ FILE: FSNotesCore/ImagesProcessor.swift ================================================ // // ImagesProcessor.swift // FSNotes // // Created by Oleksandr Glushchenko on 1/12/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation public class ImagesProcessor { public static func getFileName(from: URL? = nil, to: URL, ext: String? = nil) -> String? { let path = from?.absoluteString ?? to.absoluteString var name: String? if path.starts(with: "http://") || path.starts(with: "https://"), let webName = path.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) { name = webName } if path.starts(with: "file://") { var ext = ext ?? "jpg" var pathComponent = NSUUID().uuidString.lowercased() + "." + ext if let from = from { pathComponent = from.lastPathComponent ext = from.pathExtension } while name == nil { let destination = to.appendingPathComponent(pathComponent) let icloud = destination.appendingPathExtension("icloud") if FileManager.default.fileExists(atPath: destination.path) || FileManager.default.fileExists(atPath: icloud.path) { pathComponent = NSUUID().uuidString.lowercased() + ".\(ext)" continue } name = pathComponent } } return name } public static func writeFile(data: Data, url: URL? = nil, note: Note, ext: String? = nil) -> String? { if note.isTextBundle() { var ext = ext if ext == nil { ext = ImageFormat.get(from: data).rawValue } let assetsUrl = note.getURL().appendingPathComponent("assets") if !FileManager.default.fileExists(atPath: assetsUrl.path, isDirectory: nil) { try? FileManager.default.createDirectory(at: assetsUrl, withIntermediateDirectories: true, attributes: nil) } let destination = URL(fileURLWithPath: assetsUrl.path) guard var fileName = ImagesProcessor.getFileName(from: url, to: destination, ext: ext) else { return nil } let to = destination.appendingPathComponent(fileName) do { try data.write(to: to, options: .atomic) } catch { print(error) } fileName = fileName .addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? fileName return "assets/\(fileName)" } var prefix = "i/" if let url = url, !url.isImage { prefix = "files/" } let project = note.project let destination = URL(fileURLWithPath: project.url.path + "/" + prefix) do { try FileManager.default.createDirectory(at: destination, withIntermediateDirectories: false, attributes: nil) } catch {} guard var fileName = ImagesProcessor.getFileName(from: url, to: destination, ext: ext) else { return nil } let to = destination.appendingPathComponent(fileName) try? data.write(to: to, options: .atomic) fileName = fileName.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? fileName return "\(prefix)\(fileName)" } } ================================================ FILE: FSNotesCore/KeychainConfiguration.swift ================================================ /* Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this sample’s licensing information Abstract: A simple struct that defines the service and access group to be used by the sample apps. */ import Foundation struct KeychainConfiguration { static let serviceName = "FSNotesApp" /* Specifying an access group to use with `KeychainPasswordItem` instances will create items shared accross both apps. For information on App ID prefixes, see: https://developer.apple.com/library/ios/documentation/General/Conceptual/DevPedia-CocoaCore/AppID.html and: https://developer.apple.com/library/ios/technotes/tn2311/_index.html */ // static let accessGroup = "[YOUR APP ID PREFIX].com.example.apple-samplecode.GenericKeychainShared" /* Not specifying an access group to use with `KeychainPasswordItem` instances will create items specific to each app. */ static let accessGroup: String? = nil } ================================================ FILE: FSNotesCore/KeychainPasswordItem.swift ================================================ /* Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this sample’s licensing information Abstract: A struct for accessing generic password keychain items. */ import Foundation struct KeychainPasswordItem { // MARK: Types enum KeychainError: Error { case noPassword case unexpectedPasswordData case unexpectedItemData case unhandledError(status: OSStatus) } // MARK: Properties let service: String private(set) var account: String let accessGroup: String? // MARK: Intialization init(service: String, account: String, accessGroup: String? = nil) { self.service = service self.account = account self.accessGroup = accessGroup } // MARK: Keychain access func readPassword() throws -> String { /* Build a query to find the item that matches the service, account and access group. */ var query = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup) query[kSecMatchLimit as String] = kSecMatchLimitOne query[kSecReturnAttributes as String] = kCFBooleanTrue query[kSecReturnData as String] = kCFBooleanTrue // Try to fetch the existing keychain item that matches the query. var queryResult: AnyObject? let status = withUnsafeMutablePointer(to: &queryResult) { SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) } // Check the return status and throw an error if appropriate. guard status != errSecItemNotFound else { throw KeychainError.noPassword } guard status == noErr else { throw KeychainError.unhandledError(status: status) } // Parse the password string from the query result. guard let existingItem = queryResult as? [String: AnyObject], let passwordData = existingItem[kSecValueData as String] as? Data, let password = String(data: passwordData, encoding: String.Encoding.utf8) else { throw KeychainError.unexpectedPasswordData } return password } func savePassword(_ password: String) throws { // Encode the password into an Data object. let encodedPassword = password.data(using: String.Encoding.utf8)! do { // Check for an existing item in the keychain. try _ = readPassword() // Update the existing item with the new password. var attributesToUpdate = [String: AnyObject]() attributesToUpdate[kSecValueData as String] = encodedPassword as AnyObject? let query = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup) let status = SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary) // Throw an error if an unexpected status was returned. guard status == noErr else { throw KeychainError.unhandledError(status: status) } } catch KeychainError.noPassword { /* No password was found in the keychain. Create a dictionary to save as a new keychain item. */ var newItem = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup) newItem[kSecValueData as String] = encodedPassword as AnyObject? // Add a the new item to the keychain. let status = SecItemAdd(newItem as CFDictionary, nil) // Throw an error if an unexpected status was returned. guard status == noErr else { throw KeychainError.unhandledError(status: status) } } } mutating func renameAccount(_ newAccountName: String) throws { // Try to update an existing item with the new account name. var attributesToUpdate = [String: AnyObject]() attributesToUpdate[kSecAttrAccount as String] = newAccountName as AnyObject? let query = KeychainPasswordItem.keychainQuery(withService: service, account: self.account, accessGroup: accessGroup) let status = SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary) // Throw an error if an unexpected status was returned. guard status == noErr || status == errSecItemNotFound else { throw KeychainError.unhandledError(status: status) } self.account = newAccountName } func deleteItem() throws { // Delete the existing item from the keychain. let query = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup) let status = SecItemDelete(query as CFDictionary) // Throw an error if an unexpected status was returned. guard status == noErr || status == errSecItemNotFound else { throw KeychainError.unhandledError(status: status) } } static func passwordItems(forService service: String, accessGroup: String? = nil) throws -> [KeychainPasswordItem] { // Build a query for all items that match the service and access group. var query = KeychainPasswordItem.keychainQuery(withService: service, accessGroup: accessGroup) query[kSecMatchLimit as String] = kSecMatchLimitAll query[kSecReturnAttributes as String] = kCFBooleanTrue query[kSecReturnData as String] = kCFBooleanFalse // Fetch matching items from the keychain. var queryResult: AnyObject? let status = withUnsafeMutablePointer(to: &queryResult) { SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) } // If no items were found, return an empty array. guard status != errSecItemNotFound else { return [] } // Throw an error if an unexpected status was returned. guard status == noErr else { throw KeychainError.unhandledError(status: status) } // Cast the query result to an array of dictionaries. guard let resultData = queryResult as? [[String: AnyObject]] else { throw KeychainError.unexpectedItemData } // Create a `KeychainPasswordItem` for each dictionary in the query result. var passwordItems = [KeychainPasswordItem]() for result in resultData { guard let account = result[kSecAttrAccount as String] as? String else { throw KeychainError.unexpectedItemData } let passwordItem = KeychainPasswordItem(service: service, account: account, accessGroup: accessGroup) passwordItems.append(passwordItem) } return passwordItems } // MARK: Convenience private static func keychainQuery(withService service: String, account: String? = nil, accessGroup: String? = nil) -> [String: AnyObject] { var query = [String: AnyObject]() query[kSecClass as String] = kSecClassGenericPassword query[kSecAttrService as String] = service as AnyObject? if let account = account { query[kSecAttrAccount as String] = account as AnyObject? } if let accessGroup = accessGroup { query[kSecAttrAccessGroup as String] = accessGroup as AnyObject? } return query } } ================================================ FILE: FSNotesCore/MPreviewView.swift ================================================ // // MPreviewView.swift // FSNotes // // Created by Олександр Глущенко on 8/17/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import WebKit import SSZipArchive #if os(iOS) import MobileCoreServices import AudioToolbox #else import Carbon.HIToolbox #endif public typealias MPreviewViewClosure = () -> () class MPreviewView: WKWebView, WKUIDelegate, WKNavigationDelegate { private var editorVC: EditorViewController? private weak var note: Note? private var closure: MPreviewViewClosure? public static var template: String? init(frame: CGRect, note: Note, closure: MPreviewViewClosure?, force: Bool = false) { self.closure = closure let userContentController = WKUserContentController() userContentController.add(HandlerSelection(), name: "newSelectionDetected") let handlerCheckbox = HandlerCheckbox(note: note) userContentController.add(handlerCheckbox, name: "checkbox") userContentController.add(HandlerMouse(), name: "mouse") userContentController.add(HandlerClipboard(), name: "clipboard") let handlerOpener = HandlerOpen(note: note) userContentController.add(handlerOpener, name: "open") userContentController.add(HandlerQuickLook(), name: "quicklook") let handlerScroll = PreviewScrollHandler(note: note) userContentController.add(handlerScroll, name: "scrollPosition") let configuration = WKWebViewConfiguration() configuration.userContentController = userContentController configuration.suppressesIncrementalRendering = true super.init(frame: frame, configuration: configuration) navigationDelegate = self #if os(OSX) if #available(macOS 10.14, *) { setValue(false, forKey: "drawsBackground") } #else isOpaque = false backgroundColor = UIColor.clear scrollView.backgroundColor = UIColor.clear scrollView.bounces = true #endif load(note: note, force: force) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } public func setEditorVC(evc: EditorViewController? = nil) { self.editorVC = evc } #if os(OSX) override func mouseDown(with event: NSEvent) { guard let evc = editorVC else { super.mouseDown(with: event) return } if let note = evc.vcEditor?.note { if note.container == .encryptedTextPack && !note.isUnlocked() { evc.unLock(notes: [note]) } else if note.content.length == 0 { evc.disablePreview() evc.focusEditArea() } } super.mouseDown(with: event) } override func keyDown(with event: NSEvent) { if event.keyCode == kVK_Return { DispatchQueue.main.async { if let evc = self.editorVC { evc.disablePreview() evc.focusEditArea() } } return } super.keyDown(with: event) } override func performKeyEquivalent(with event: NSEvent) -> Bool { if event.characters?.unicodeScalars.first == "c" && event.modifierFlags.contains(.command) { DispatchQueue.main.async { guard let string = HandlerSelection.selectionString else { return } let pasteboard = NSPasteboard.general pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) pasteboard.setString(string, forType: NSPasteboard.PasteboardType.string) } return false } return super.performKeyEquivalent(with: event) } override func willOpenMenu(_ menu: NSMenu, with event: NSEvent) { for item in menu.items { if item.identifier?.rawValue == "WKMenuItemIdentifierReload" { item.isHidden = true } } } #endif public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { closure?() } public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { guard let url = navigationAction.request.url else { return } switch navigationAction.navigationType { case .linkActivated: decisionHandler(.cancel) if isFootNotes(url: url) { return } #if os(iOS) if url.absoluteString.starts(with: "fsnotes://find?id=") { UIApplication.getEVC().openWikiLink(query: url.absoluteString) return } UIApplication.shared.open(url, options: [:], completionHandler: nil) #elseif os(OSX) NSWorkspace.shared.open(url) #endif default: decisionHandler(.allow) } } public static func loadAttachments(html: String, note: Note, showButton: Bool = true) -> String { guard let urls = note.attachments, urls.count > 0 else { return html } var htmlString = html var imagesStorage = note.project.url if note.isTextBundle() { imagesStorage = note.getURL() } do { let regex = try NSRegularExpression(pattern: " 0 { cleanCache() let dst = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("wkPreview") if let i = MPreviewView.buildPage(for: note, at: dst) { if getppid() != 1 { print("Web view loaded from: \(i)") } let accessURL = i.deletingLastPathComponent() loadFileURL(i, allowingReadAccessTo: accessURL) } } else { var htmlString = renderMarkdownHTML(markdown: markdownString)! htmlString = MPreviewView.loadAttachments(html: htmlString, note: note) if let pageHTMLString = try? MPreviewView.htmlFromTemplate(htmlString), let baseURL = Bundle.main.url(forResource: "MPreview", withExtension: "bundle") { loadHTMLString(pageHTMLString, baseURL: baseURL) } } } public func cleanCache() { URLCache.shared.removeAllCachedResponses() DispatchQueue.global(qos: .background).async { HTTPCookieStorage.shared.removeCookies(since: Date.distantPast) } WKWebsiteDataStore.default().fetchDataRecords(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes()) { records in records.forEach { record in WKWebsiteDataStore.default().removeData(ofTypes: record.dataTypes, for: [record], completionHandler: {}) } } } public static func getMathJaxJS() -> String { if !UserDefaultsManagement.mathJaxPreview { return String() } return """ """ } private func isFootNotes(url: URL) -> Bool { let link = url.absoluteString.components(separatedBy: "/index.html#") if link.count == 2 { openAnchor(anchor: link[1]) return true } let bundleLink = url.absoluteString.components(separatedBy: "/MPreview.bundle/#") if bundleLink.count == 2 { openAnchor(anchor: bundleLink[1]) return true } return false } private func openAnchor(anchor: String) { evaluateJavaScript("document.getElementById('\(anchor)').offsetTop") { [weak self] (result, error) in if let offset = result as? CGFloat { self?.evaluateJavaScript("window.scrollTo(0,\(offset))", completionHandler: nil) } } evaluateJavaScript("getElementsByText('\(anchor)')[0].offsetTop") { [weak self] (result, error) in if let offset = result as? CGFloat { self?.evaluateJavaScript("window.scrollTo(0,\(offset))", completionHandler: nil) } } let textQuery = anchor.replacingOccurrences(of: "-", with: " ") evaluateJavaScript("getElementsByTextContent('\(textQuery)').offsetTop") { [weak self] (result, error) in if let offset = result as? CGFloat { self?.evaluateJavaScript("window.scrollTo(0,\(offset))", completionHandler: nil) } } } public static func buildPage(for note: Note, at dst: URL, web: Bool = false, print: Bool = false) -> URL? { var markdownString = note.getPrettifiedContent() // Hack for WebView compatibility if print { markdownString = MPreviewView.assignBase64Images(note: note, html: markdownString) } var htmlString = renderMarkdownHTML(markdown: markdownString)! var imagesStorage = note.project.url if note.isTextBundle() { imagesStorage = note.getURL() } var webPath: String? var zipName: String? // For uploaded content if web { // Generate zip zipName = "\(note.getLatinName()).zip" let zipURL = dst.appendingPathComponent(note.getLatinName()).appendingPathExtension("zip") try? FileManager.default.createDirectory(at: dst, withIntermediateDirectories: true, attributes: nil) if note.container == .none { SSZipArchive.createZipFile(atPath: zipURL.path, withFilesAtPaths: [note.url.path]) } else { SSZipArchive.createZipFile(atPath: zipURL.path, withContentsOfDirectory: note.url.path, keepParentDirectory: true) } if UserDefaultsManagement.customWebServer { webPath = UserDefaultsManagement.sftpWeb } else { webPath = UserDefaultsManagement.webPath } } let state = !(web || print) htmlString = MPreviewView.loadAttachments(html: htmlString, note: note, showButton: state) if let urls = note.imageUrl, urls.count > 0, !print { htmlString = MPreviewView.loadImages(imagesStorage: imagesStorage, html: htmlString, at: dst, web: web) } if let pageHTMLString = try? htmlFromTemplate(htmlString, webPath: webPath, print: print, archivePath: zipName, note: note) { let indexURL = createTemporaryBundle(pageHTMLString: pageHTMLString, at: dst) return indexURL } return nil } public static func createTemporaryBundle(pageHTMLString: String, at: URL) -> URL? { let path = Bundle.main.path(forResource: "MPreview", ofType: ".bundle") let url = NSURL.fileURL(withPath: path!) let bundle = Bundle(url: url) guard let bundleResourceURL = bundle?.resourceURL else { return nil } let webkitPreview = at try? FileManager.default.createDirectory(at: webkitPreview, withIntermediateDirectories: true, attributes: nil) let indexURL = webkitPreview.appendingPathComponent("index.html") let mainCssUrl = webkitPreview.appendingPathComponent("main.css") // If updating markdown contents, no need to re-copy bundle. if !FileManager.default.fileExists(atPath: indexURL.path) || !FileManager.default.fileExists(atPath: mainCssUrl.path) { // Copy bundle resources to temporary location. do { let fileList = try FileManager.default.contentsOfDirectory(atPath: bundleResourceURL.path) for file in fileList { let tmpURL = webkitPreview.appendingPathComponent(file) if ["css", "js"].contains(file) { try? FileManager.default.removeItem(at: tmpURL) } try? FileManager.default.copyItem(atPath: bundleResourceURL.appendingPathComponent(file).path, toPath: tmpURL.path) } } catch { print(error) } } // Write generated index.html to temporary location. try? pageHTMLString.write(to: indexURL, atomically: true, encoding: .utf8) return indexURL } public static func loadImages(imagesStorage: URL, html: String, at: URL, web: Bool = false) -> String { var htmlString = html do { let regex = try NSRegularExpression(pattern: " String { let webPath = webPath ?? "" var htmlString = htmlString let path = Bundle.main.path(forResource: "MPreview", ofType: ".bundle") let url = NSURL.fileURL(withPath: path!) let bundle = Bundle(url: url) let baseURL = bundle!.url(forResource: "index", withExtension: "html")! var template = try String(contentsOf: baseURL, encoding: .utf8) var platform = String() var appearance = String() let isWeb = webPath.count > 0 let preview = String(webPath.count == 0) #if os(iOS) platform = "ios" if UITraitCollection.current.userInterfaceStyle == .dark && archivePath == nil { appearance = "darkmode" } #else platform = "macos" if UserDataService.instance.isDark && archivePath == nil && print == false { appearance = "darkmode" } #endif if webPath.count > 0 { htmlString = """
\(htmlString)
""" } var title = String() if let unwrapped = note?.getTitle() { title = unwrapped } let inlineCss = MPreviewView.getPreviewStyle(print: print, forceLightTheme: isWeb) template = template .replacingOccurrences(of: "{TITLE}", with: title) .replacingOccurrences(of: "{INLINE_CSS}", with: inlineCss) .replacingOccurrences(of: "{MATH_JAX_JS}", with: MPreviewView.getMathJaxJS()) .replacingOccurrences(of: "{FSNOTES_APPEARANCE}", with: appearance) .replacingOccurrences(of: "{FSNOTES_PLATFORM}", with: platform) .replacingOccurrences(of: "{FSNOTES_PREVIEW}", with: preview) .replacingOccurrences(of: "{NOTE_BODY}", with: htmlString) .replacingOccurrences(of: "{WEB_PATH}", with: webPath) return template } public static func getPreviewStyle(print: Bool = false, forceLightTheme: Bool = false) -> String { var theme: String? = nil var fullScreen = false var useFixedImageHeight = true var css = "" return css } public static func assignBase64Images(note: Note, html: String) -> String { var html = html FSParser.imageInlineRegex.regularExpression.enumerateMatches(in: note.content.string, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSRange(0.. Void in guard let range = result?.range(at: 3), note.content.length >= range.location else { return } let path = note.content.attributedSubstring(from: range).string guard let imagePath = path.removingPercentEncoding else { return } if let url = note.getAttachmentFileUrl(name: imagePath) { if url.isRemote() { return } if FileManager.default.fileExists(atPath: url.path), url.isImage { if let image = try? Data(contentsOf: url) { let base64 = image.base64EncodedString() html = html.replacingOccurrences(of: path, with: "data:image;base64," + base64) } } } }) return html } public func clean() { loadHTMLString("", baseURL: nil) } private static func computeDefaultLineHeight(for font: Font, lineHeightMultiple: CGFloat = 1.0) -> CGFloat { let asc = font.ascender let desc = abs(font.descender) let lead = font.leading let base = asc + desc + lead return base * lineHeightMultiple } } class HandlerSelection: NSObject, WKScriptMessageHandler { public static var selectionString: String? func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { let message = (message.body as! String).trimmingCharacters(in: .whitespacesAndNewlines) HandlerSelection.selectionString = message } } class HandlerCheckbox: NSObject, WKScriptMessageHandler { private var note: Note? init(note: Note) { self.note = note } func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { guard let position = message.body as? String else { return } guard let note = self.note else { return } let content = note.content.unloadAttachments() let string = content.string let range = NSRange(0.. Void in guard let range = result?.range else { return } if i == Int(position) { let substring = content.mutableString.substring(with: range) if substring.contains("- [x] ") { content.replaceCharacters(in: range, with: "- [ ] ") } else { content.replaceCharacters(in: range, with: "- [x] ") } #if os(iOS) AudioServicesPlaySystemSound(1519) #endif } i = i + 1 } note.save(content: content.loadAttachments(note)) } } class HandlerMouse: NSObject, WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { guard let action = message.body as? String else { return } #if os(OSX) if action == "enter" { NSCursor.pointingHand.set() } else { NSCursor.arrow.set() } #endif } } class HandlerClipboard: NSObject, WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { guard let action = message.body as? String else { return } var cleanText = action.trim() if cleanText.last == "\n" { cleanText.removeLast() } #if os(OSX) NSPasteboard.general.clearContents() NSPasteboard.general.setString(cleanText, forType: .string) #else UIPasteboard.general.setItems([ [kUTTypePlainText as String: cleanText] ]) #endif } } class HandlerOpen: NSObject, WKScriptMessageHandler { private var note: Note? init(note: Note) { self.note = note } func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { guard let note = note else { return } guard let action = message.body as? String else { return } var cleanText = action.trim() cleanText = cleanText.removingPercentEncoding ?? cleanText if cleanText.contains("wkPreview/index.html") || cleanText.contains("MPreview.bundle/index.html") || cleanText.contains("MPreview.bundle/#") { return } #if os(OSX) let result = cleanText.replacingOccurrences( of: "^.*?/(tmp/wkPreview|Resources/MPreview\\.bundle)/", with: "", options: .regularExpression ) if let url = result.createURL(for: note) { NSWorkspace.shared.activateFileViewerSelecting([url]) } #endif } } class HandlerQuickLook: NSObject, WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { guard let action = message.body as? String else { return } let cleanText = "file://" + action.trim() if let url = URL(string: cleanText) { #if os(iOS) UIApplication.getEVC().quickLook(url: url) #else NSWorkspace.shared.activateFileViewerSelecting([url]) #endif } } } final class PreviewScrollHandler: NSObject, WKScriptMessageHandler { private var note: Note? init(note: Note) { self.note = note } func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { guard message.name == "scrollPosition", let dict = message.body as? [String: Double], let x = dict["x"], let y = dict["y"] else { return } note?.contentOffsetWeb = CGPoint(x: x, y: y) } } ================================================ FILE: FSNotesCore/NSTextAttachment+.swift ================================================ // // NSTextAttachment+.swift // FSNotes // // Created by Олександр Глущенко on 10/2/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // #if os(OSX) import AppKit #else import UIKit #endif import UniformTypeIdentifiers extension NSTextAttachment { public func isFile() -> Bool { #if os(iOS) return false #endif #if os(OSX) return (attachmentCell?.cellSize().height == 30) #endif } } ================================================ FILE: FSNotesCore/NSTextStorage++.swift ================================================ // // CustomTextStorage.swift // FSNotes // // Created by Oleksandr Glushchenko on 10/12/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // #if os(OSX) import AppKit #else import UIKit #endif extension NSTextStorage { #if os(OSX) public var highlightColor: NSColor { get { return NSColor(named: "highlight")! } } #else public var highlightColor: UIColor { get { return UIColor.highlightColor } } #endif public func getImageRange(url: URL) -> NSRange? { let affectedRange = NSRange(0.. "] mutableString.enumerateSubstrings(in: paragraphRange, options: .byParagraphs) { value, parRange, _, _ in guard let value = value else { return } let paragraph = NSMutableParagraphStyle() paragraph.lineSpacing = lineSpacing paragraph.alignment = .left paragraph.tabStops = tabs if value.count > 1 { let prefix = value.getSpacePrefix() var matchedPrefix: String? if prefix.isEmpty { for marker in markers { if value.hasPrefix(marker) { matchedPrefix = marker break } } } else { for marker in markers { let fullMarker = prefix + marker if value.hasPrefix(fullMarker) { matchedPrefix = fullMarker break } } } if matchedPrefix == nil { matchedPrefix = self.getNumberListPrefix(paragraph: value) } if let prefix = matchedPrefix { paragraph.headIndent = prefix.widthOfString(usingFont: font, tabs: tabs) } } self.addAttribute(.paragraphStyle, value: paragraph, range: parRange) } } public func getTabStops() -> [NSTextTab] { var tabs = [NSTextTab]() let tabInterval = 40 for index in 1...25 { let tab = NSTextTab(textAlignment: .left, location: CGFloat(tabInterval * index), options: [:]) tabs.append(tab) } return tabs } private static let numberListRegex = try? NSRegularExpression( pattern: #"^(\s*)(\d+)(\.)(\s+)"#, options: [] ) public func getNumberListPrefix(paragraph: String) -> String? { guard !paragraph.isEmpty else { return nil } let nsString = paragraph as NSString let range = NSRange(location: 0, length: min(nsString.length, 20)) if let match = Self.numberListRegex?.firstMatch(in: paragraph, options: [], range: range) { return nsString.substring(with: match.range) } return nil } public func updateCheckboxList() { let fullRange = NSRange(location: 0, length: self.length) enumerateAttribute(.todo, in: fullRange, options: []) { value, range, _ in if let value = value as? Int { let attribute = self.attribute(.attachment, at: range.location, longestEffectiveRange: nil, in: fullRange) if let attachment = attribute as? NSTextAttachment { let checkboxName = value == 0 ? "checkbox_empty" : "checkbox" attachment.image = AttributedBox.getImage(name: checkboxName) for layoutManager in layoutManagers { layoutManager.invalidateDisplay(forCharacterRange: range) } } } } } public func highlightKeyword(search: String) { var search = search guard search.count > 1, UserDefaultsManagement.searchHighlight else { return } if search.hasPrefix("\"") && search.hasSuffix("\"") { let clean = String(search.dropFirst().dropLast()) if clean.count > 0 { search = clean } } let searchTerm = NSRegularExpression.escapedPattern(for: search) let pattern = "(\(searchTerm))" let range = NSRange(location: 0, length: length) do { let regex = try NSRegularExpression(pattern: pattern, options: [.caseInsensitive]) let matches = regex.matches(in: self.string, options: [], range: range) self.beginEditing() for match in matches { let subRange = match.range guard subRange.location < self.length else { continue } if let currentBackgroundColor = self.attribute(.backgroundColor, at: subRange.location, effectiveRange: nil) { self.addAttribute(.highlight, value: currentBackgroundColor, range: subRange) } else { self.addAttribute(.highlight, value: NSNull(), range: subRange) } self.addAttribute(.backgroundColor, value: self.highlightColor, range: subRange) } self.endEditing() } catch { print(error) } } public func removeHighlight() { let range = NSRange(location: 0, length: length) self.beginEditing() self.enumerateAttribute( .highlight, in: range, options: [] ) { value, subRange, _ in guard value != nil else { return } #if os(macOS) if let originalColor = value as? NSColor { self.addAttribute(.backgroundColor, value: originalColor, range: subRange) } else { self.removeAttribute(.backgroundColor, range: subRange) } #else if let originalColor = value as? UIColor { self.addAttribute(.backgroundColor, value: originalColor, range: subRange) } else { self.removeAttribute(.backgroundColor, range: subRange) } #endif self.removeAttribute(.highlight, range: subRange) } self.endEditing() } } ================================================ FILE: FSNotesCore/NameHelper.swift ================================================ // // File.swift // FSNotes // // Created by Oleksandr Glushchenko on 3/4/19. // Copyright © 2019 Oleksandr Glushchenko. All rights reserved. // import Foundation class NameHelper { public static func getUniqueFileName(name: String, postfix: Int = 1, project: Project, ext: String) -> URL { var defaultName = UUID().uuidString if let naming = SettingsFilesNaming(rawValue: UserDefaultsManagement.naming.rawValue) { defaultName = naming.getName() } var postfix = postfix var name = name .trimmingCharacters(in: CharacterSet.whitespaces) .replacingOccurrences(of: ":", with: "") .replacingOccurrences(of: "/", with: "") if name.isEmpty { name = defaultName } var fileUrl = project.url fileUrl.appendPathComponent(name + "." + ext, isDirectory: false) let fileManager = FileManager.default if fileManager.fileExists(atPath: fileUrl.path) { let regex = try? NSRegularExpression(pattern: "(.+)\\s(\\d)+$", options: .caseInsensitive) if let result = regex?.firstMatch(in: name, range: NSRange(0.. URL { let dst = dstDir ?? file.deletingLastPathComponent() let ext = file.pathExtension var name = file.deletingPathExtension().lastPathComponent let regex = try? NSRegularExpression(pattern: "(.+)\\s(?:Copy\\s)+(?:\\d)+$", options: .caseInsensitive) if let result = regex?.firstMatch(in: name, range: NSRange(0.. 1 { endName += " " + String(number) } let newDst = dst.appendingPathComponent(endName + "." + ext, isDirectory: false) if !FileManager.default.fileExists(atPath: newDst.path) { return newDst } return generateCopy(file: file, dstDir: dst, number: number + 1) } } ================================================ FILE: FSNotesCore/Note+History.swift ================================================ // // Note+History.swift // FSNotes iOS // // Created by Александр on 14.02.2022. // Copyright © 2022 Oleksandr Glushchenko. All rights reserved. // import Foundation import Compression extension Note { public func getGitPath(history: Bool = false) -> String { var path = name if let gitPath = getGitPathPrefix() { path = gitPath } if history && isTextBundle(), let contentURL = getContentFileURL() { path += "/" + contentURL.lastPathComponent } return path.recode4byteString() } public func getGitPathPrefix() -> String? { guard let project = getGitProject() else { return nil } let relative = url.path.replacingOccurrences(of: project.url.path, with: "") if !UserDefaultsManagement.iCloudDrive && relative.startsWith(string: "/private/") { return relative.replacingOccurrences(of: "/private/", with: "") } if relative.first == "/" { return String(relative.dropFirst()) } if relative == "" { return nil } return relative } public func hasGitRepository() -> Bool { return project.getGitProject() != nil } public func getGitProject() -> Project? { return project.getGitProject() } public func saveRevision() throws { guard hasGitRepository() else { return } guard let project = getGitProject() else { return } try project.saveRevision(commitMessage: nil) } public func dropRevisions() { do { if let repository = getRepositoryUrl() { try FileManager.default.removeItem(at: repository) } } catch {/*_*/} } public func restore(revision: Revision) { guard hasGitRepository() else { return } checkout(commit: revision.commit!) forceLoad() } public func listRevisions() -> [Revision] { guard hasGitRepository() else { return [Revision]() } var result = [Revision]() let commits = getCommits() for commit in commits { let timestamp = commit.date.timeIntervalSince1970 result.append(Revision(timestamp: timestamp, commit: commit)) } return result } private func getRepositoryUrl() -> URL? { guard let url = project.getHistoryURL() else { return nil } return url.appendingPathComponent(name) } public func moveHistory(src: URL, dst: URL) { let srcFileName = src.lastPathComponent let dstFileName = dst.lastPathComponent var srcProject = project.getHistoryURL() var dstProject = project.getHistoryURL() if let dstHistory = project.storage.getProjectBy(url: dst.deletingLastPathComponent())?.getHistoryURL() { if !FileManager.default.directoryExists(atUrl: dstHistory) { try? FileManager.default.createDirectory(at: dstHistory, withIntermediateDirectories: true, attributes: nil) } dstProject = dstHistory } if let srcHistory = project.storage.getProjectBy(url: src.deletingLastPathComponent())?.getHistoryURL(), FileManager.default.directoryExists(atUrl: srcHistory) { srcProject = srcHistory } guard let srcDir = srcProject?.appendingPathComponent(srcFileName), FileManager.default.fileExists(atPath: srcDir.path), let dstDir = dstProject?.appendingPathComponent(dstFileName), !FileManager.default.directoryExists(atUrl: dstDir) else { return } do { try FileManager.default.moveItem(at: srcDir, to: dstDir) } catch { print("History transfer \(error)") } } public func getCommits() -> [Commit] { var commits = [Commit]() do { guard let project = getGitProject(), project.hasCommitsDiffsCache() else { return commits } let repository = try project.getRepository() let path = getGitPath(history: true) do { let fileRevLog = try FileHistoryIterator(repository: repository, path: path, project: project) let oids = fileRevLog.walk() for oid in oids { if let commit = try? repository.commitLookup(oid: oid) { commits.append(commit) } } if fileRevLog.checkFirstCommit() { if let oid = fileRevLog.getLast(), let commit = try? repository.commitLookup(oid: oid) { commits.append(commit) } } } catch {/*_*/} return commits } catch { print(error) } return commits } public func checkout(commit: Commit) { do { guard let repository = try getGitProject()?.getRepository() else { return } let commit = try repository.commitLookup(oid: commit.oid) try repository.checkout(commit: commit, path: getGitPath()) print("Successful checkout") } catch { print(error) } } } public struct Revision { var timestamp: Double var url: URL? var commit: Commit? } ================================================ FILE: FSNotesCore/NoteCellView+.swift ================================================ // // NoteCellView+.swift // FSNotes // // Created by Oleksandr Glushchenko on 11/6/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // #if os(iOS) import UIKit typealias ImageView = UIImageView #else import Cocoa typealias ImageView = NSImageView #endif extension NoteCellView { public func loadImagesPreview(position: Int? = nil, urls: [URL]? = nil) { guard let note = self.note else { hideUnusedImagesPreview() imageKeys = [] return } note.loadPreviewInfo() guard !UserDefaultsManagement.hidePreviewImages && !UserDefaultsManagement.horizontalOrientation else { hideUnusedImagesPreview() imageKeys = [] return } let imageURLs = urls ?? note.imageUrl guard let imageURLs = imageURLs, !imageURLs.isEmpty else { hideUnusedImagesPreview() imageKeys = [] attachHeaders(note: note) fixTopConstraint(position: position, note: note) return } let isNotAssigned = imagePreview.image == nil && imagePreviewSecond.image == nil && imagePreviewThird.image == nil let isAssigned = imagePreview.image != nil || imagePreviewSecond.image != nil || imagePreviewThird.image != nil let needsReload = isImagesChanged(imageURLs: imageURLs) if !needsReload && !isNotAssigned { attachHeaders(note: note) fixTopConstraint(position: position, note: note) return } if needsReload || (isNotAssigned && isAssigned) { hideUnusedImagesPreview() imageKeys = [] } DispatchQueue.global(qos: .userInteractive).async { [weak self] in guard let self = self else { return } let current = Date().toMillis() self.timestamp = current var paths = [String]() let resizedImages = self.getResizedPreviewImages(note: note, images: imageURLs, timestamp: current!) DispatchQueue.main.async { [weak self] in guard let self = self else { return } if current != self.timestamp { return } guard self.note === note else { return } for imageUrl in imageURLs { paths.append(imageUrl.path) } self.imageKeys = paths self.attachImagesPreview(resizedImages: resizedImages) self.fixTopConstraint(position: position, note: note) } } } private func isImagesChanged(imageURLs: [URL]? = nil) -> Bool { guard let imageURLs = imageURLs else { return !imageKeys.isEmpty } if imageURLs.count != imageKeys.count { return true } let newPaths = Set(imageURLs.map { $0.path }) let currentPaths = Set(imageKeys) return newPaths != currentPaths } private func hideUnusedImagesPreview() { self.imagePreviewThird.image = nil self.imagePreviewThird.isHidden = true self.imagePreviewSecond.image = nil self.imagePreviewSecond.isHidden = true self.imagePreview.image = nil self.imagePreview.isHidden = true } private func attachImagesPreview(resizedImages: [Image]) { var index = 0 for resized in resizedImages { index += 1 switch index { case 1: self.imagePreview.image = resized self.styleImageView(imageView: self.imagePreview) case 2: self.imagePreviewSecond.image = resized self.styleImageView(imageView: self.imagePreviewSecond) case 3: self.imagePreviewThird.image = resized self.styleImageView(imageView: self.imagePreviewThird) default: break } } if resizedImages.count < 3 { self.imagePreviewThird.image = nil self.imagePreviewThird.isHidden = true } if resizedImages.count < 2 { self.imagePreviewSecond.image = nil self.imagePreviewSecond.isHidden = true } if resizedImages.count < 1 { self.imagePreview.image = nil self.imagePreview.isHidden = true } #if os(macOS) self.needsDisplay = true self.needsLayout = true self.imagePreview.needsDisplay = true self.imagePreviewSecond.needsDisplay = true self.imagePreviewThird.needsDisplay = true self.layoutSubtreeIfNeeded() self.superview?.needsLayout = true self.superview?.layoutSubtreeIfNeeded() #endif } public func getResizedPreviewImages(note: Note, images: [URL], timestamp: Int64 = 00) -> [Image] { var resizedImages: [Image] = [] for imageUrl in images { if timestamp != self.timestamp { return [] } if let image = getPreviewImage(imageUrl: imageUrl, note: note) { resizedImages.append(image) } } return resizedImages } } ================================================ FILE: FSNotesCore/NoteMeta.swift ================================================ // // NoteMeta.swift // FSNotes // // Created by Олександр Глущенко on 17.05.2020. // Copyright © 2020 Oleksandr Glushchenko. All rights reserved. // import Foundation public struct NoteMeta: Codable { var url: URL var attachments: [URL]? var imageUrl: [URL]? var title: String var preview: String var modificationDate: Date var creationDate: Date var pinned: Bool var tags: [String] var selectedRange: NSRange? } ================================================ FILE: FSNotesCore/NotesTextProcessor.swift ================================================ // // NotesTextStorage.swift // FSNotes // // Created by Oleksandr Glushchenko on 12/26/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // #if os(OSX) import Cocoa import MASShortcut #else import UIKit #endif public class NotesTextProcessor { #if os(OSX) typealias Color = NSColor typealias Image = NSImage typealias Font = NSFont public static var fontColor: NSColor { get { return NSColor(named: "mainText")! } } #else typealias Color = UIColor typealias Image = UIImage typealias Font = UIFont public static var fontColor: UIColor { get { return UIColor { (traits) -> UIColor in return traits.userInterfaceStyle == .dark ? UIColor.white : UIColor.black } } } #endif // MARK: Syntax highlight customisation /** Color used to highlight markdown syntax. Default value is light grey. */ public static var syntaxColor = Color.lightGray public static var yamlOpenerColor = Color.systemRed public static var codeBackground: PlatformColor { get { let isDark = UserDataService.instance.isDark let editorTheme = UserDefaultsManagement.codeTheme.makeStyle(isDark: isDark) return editorTheme.backgroundColor } } #if os(OSX) public static var font: NSFont { get { return UserDefaultsManagement.noteFont } } public static var codeSpanBackground: NSColor { get { return NSColor(named: "code") ?? NSColor(red:0.97, green:0.97, blue:0.97, alpha:1.0) } } public static var quoteColor: NSColor { get { return NSColor(named: "quoteColor")! } } #else public static var font: UIFont { get { return UserDefaultsManagement.noteFont } } public static var codeSpanBackground: UIColor { get { return UIColor.codeBackground } } public static var quoteColor: UIColor { get { return UIColor.darkGray } } #endif /** Quote indentation in points. Default 20. */ open var quoteIndendation : CGFloat = 20 static var codeFont = UserDefaultsManagement.codeFont /** If the markdown syntax should be hidden or visible */ public static var hideSyntax = false private var note: Note? private var storage: NSTextStorage? private var range: NSRange? private var width: CGFloat? public static var hl: SwiftHighlighter? = nil init(note: Note? = nil, storage: NSTextStorage? = nil, range: NSRange? = nil) { self.note = note self.storage = storage self.range = range } public static func getHighlighter() -> SwiftHighlighter { if let instance = self.hl { return instance } let isDark = UserDataService.instance.isDark let style = UserDefaultsManagement.codeTheme.makeStyle(isDark: isDark) let highlighter = SwiftHighlighter(options: .init(style: style)) self.hl = highlighter return highlighter } public static func resetCaches() { NotesTextProcessor.hl = nil NotesTextProcessor.codeFont = UserDefaultsManagement.codeFont } public static func getSpanCodeBlockRange(content: NSMutableAttributedString, range: NSRange) -> NSRange? { var codeSpan: NSRange? let paragraphRange = content.mutableString.paragraphRange(for: range) let paragraph = content.attributedSubstring(from: paragraphRange).string if paragraph.contains("`") { NotesTextProcessor.codeSpanRegex.matches(content.string, range: paragraphRange) { (result) -> Void in if let spanRange = result?.range, spanRange.intersection(range) != nil { codeSpan = spanRange } } } return codeSpan } fileprivate static var quoteIndendationStyle : NSParagraphStyle { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = CGFloat(UserDefaultsManagement.editorLineSpacing) return paragraphStyle } /** Coverts App links:`[[Link Title]]` to Markdown: `[Link](fsnotes://find/link%20title)` - parameter content: A string containing CommonMark Markdown - returns: Content string with converted links */ public static func convertAppLinks(in content: NSMutableAttributedString, codeBlockRanges: [NSRange]?) -> NSMutableAttributedString { let attributedString = content.mutableCopy() as! NSMutableAttributedString let range = NSRange(0.. (Void) in guard let innerRange = result?.range else { return } var substring = attributedString.mutableString.substring(with: innerRange) substring = substring .replacingOccurrences(of: "[[", with: "") .replacingOccurrences(of: "]]", with: "") .trim() guard let tag = substring.addingPercentEncoding(withAllowedCharacters: .alphanumerics) else { return } attributedString.addAttribute(.link, value: "\(tagQuery)\(tag)", range: innerRange) }) attributedString.enumerateAttribute(.link, in: range) { (value, range, _) in if let value = value as? String, value.starts(with: tagQuery) { if let tag = value .replacingOccurrences(of: tagQuery, with: "") .removingPercentEncoding { if NotesTextProcessor.getSpanCodeBlockRange(content: attributedString, range: range) != nil { return } if let codeRanges = codeBlockRanges { for codeRange in codeRanges { if NSIntersectionRange(codeRange, range).length > 0 { return } } } let link = "[\(tag)](\(value))" attributedString.replaceCharacters(in: range, with: link) } } } return attributedString } public static func convertAppTags(in content: NSMutableAttributedString, codeBlockRanges: [NSRange]?) -> NSMutableAttributedString { let attributedString = content.mutableCopy() as! NSMutableAttributedString guard UserDefaultsManagement.inlineTags else { return attributedString} let range = NSRange(0.. Void in guard var range = result?.range(at: 1) else { return } var substring = attributedString.mutableString.substring(with: range) guard !substring.isNumber else { return } range = NSRange(location: range.location - 1, length: range.length + 1) substring = attributedString.mutableString.substring(with: range) .replacingOccurrences(of: "#", with: "") .replacingOccurrences(of: "\n", with: "") .trim() guard let tag = substring.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else { return } attributedString.addAttribute(.link, value: "\(tagQuery)\(tag)", range: range) } attributedString.enumerateAttribute(.link, in: range) { (value, range, _) in if let value = value as? String, value.starts(with: tagQuery) { if let tag = value .replacingOccurrences(of: tagQuery, with: "") .removingPercentEncoding { if NotesTextProcessor.getSpanCodeBlockRange(content: attributedString, range: range) != nil { return } if let codeRanges = codeBlockRanges { for codeRange in codeRanges { if NSIntersectionRange(codeRange, range).length > 0 { return } } } let link = "[#\(tag)](\(value))" attributedString.replaceCharacters(in: range, with: link) } } } return attributedString } public static func highlight(attributedString: NSMutableAttributedString) { let ranges = CodeBlockDetector.shared.findCodeBlocks(in: attributedString) NotesTextProcessor.highlightMarkdown(attributedString: attributedString, codeBlockRanges: ranges) for range in ranges { NotesTextProcessor .getHighlighter() .highlight(in: attributedString, fullRange: range) } } public static func removeFontTraits( _ traitsToRemove: FontTraits, range: NSRange, attributedString: NSMutableAttributedString ) { let baseFont = UserDefaultsManagement.noteFont let pointSize = baseFont.pointSize attributedString.enumerateAttribute(.font, in: range) { value, subrange, _ in guard let font = value as? PlatformFont else { return } let currentTraits = font.fontDescriptor.symbolicTraits guard !currentTraits.isDisjoint(with: traitsToRemove) else { return } let newTraits = currentTraits.subtracting(traitsToRemove) let newDesc = font.fontDescriptor.withSymbolicTraits(newTraits) #if os(iOS) guard let newDesc = newDesc else { return } let newFont = PlatformFont(descriptor: newDesc, size: pointSize) #else guard let newFont = PlatformFont(descriptor: newDesc, size: pointSize) else { return } #endif attributedString.addAttribute(.font, value: newFont, range: subrange) } } public static func addFontTraits( _ traitsToAdd: FontTraits, range: NSRange, attributedString: NSMutableAttributedString ) { attributedString.enumerateAttribute(.font, in: range) { value, subrange, _ in guard let font = (value as? PlatformFont) else { return } let currentTraits = font.fontDescriptor.symbolicTraits let newTraits = currentTraits.union(traitsToAdd) let newDesc = font.fontDescriptor.withSymbolicTraits(newTraits) #if os(iOS) guard let newDesc = newDesc else { return } let newFont = PlatformFont(descriptor: newDesc, size: font.pointSize) #else guard let newFont = PlatformFont(descriptor: newDesc, size: font.pointSize) else { return } #endif attributedString.addAttribute(.font, value: newFont, range: subrange) } } public static func resetFont(attributedString: NSMutableAttributedString, paragraphRange: NSRange) { attributedString.addAttribute(.font, value: font, range: paragraphRange) attributedString.fixAttributes(in: paragraphRange) } public static func highlightMarkdown(attributedString: NSMutableAttributedString, paragraphRange: NSRange? = nil, codeBlockRanges: [NSRange]? = nil) { let paragraphRange = paragraphRange ?? NSRange(0.. NSRange) { guard NotesTextProcessor.hideSyntax else { return } attributedString.addAttributes(hiddenAttributes, range: range()) } attributedString.enumerateAttribute(.link, in: paragraphRange, options: []) { (value, range, stop) -> Void in if value != nil && attributedString.attribute(.attachment, at: range.location, effectiveRange: nil) == nil { attributedString.removeAttribute(.link, range: range) } } attributedString.enumerateAttribute(.strikethroughStyle, in: paragraphRange, options: []) { (value, range, stop) -> Void in if value != nil { attributedString.removeAttribute(.strikethroughStyle, range: range) } } attributedString.enumerateAttribute(.tag, in: paragraphRange, options: []) { (value, range, stop) -> Void in if value != nil { attributedString.removeAttribute(.tag, range: range) } } #if os(iOS) attributedString.addAttribute(.foregroundColor, value: UIColor.blackWhite, range: paragraphRange) #else attributedString.addAttribute(.foregroundColor, value: fontColor, range: paragraphRange) attributedString.enumerateAttribute(.foregroundColor, in: paragraphRange, options: []) { (value, range, stop) -> Void in if (value as? NSColor) != nil { attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.fontColor, range: range) } } #endif // We detect and process inline links not formatted NotesTextProcessor.autolinkRegex.matches(string, range: paragraphRange) { (result) -> Void in guard var range = result?.range else { return } var substring = attributedString.mutableString.substring(with: range) guard substring.lengthOfBytes(using: .utf8) > 0 else { return } if ["!", "?", ";", ":", ".", ",", "_"].contains(substring.last) { range = NSRange(location: range.location, length: range.length - 1) substring = String(substring.dropLast()) } if substring.first == "(" { range = NSRange(location: range.location + 1, length: range.length - 1) } if substring.last == ")" { range = NSRange(location: range.location, length: range.length - 1) } if let url = URL(string: substring) { attributedString.addAttribute(.link, value: url, range: range) } else if let substring = String(substring).addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) { attributedString.addAttribute(.link, value: substring, range: range) } if NotesTextProcessor.hideSyntax { NotesTextProcessor.autolinkPrefixRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.font, value: hiddenFont, range: innerRange) attributedString.fixAttributes(in: innerRange) attributedString.addAttribute(.foregroundColor, value: hiddenColor, range: innerRange) } } } FSParser.yamlBlockRegex.matches(string, range: NSRange(location: 0, length: attributedString.length)) { (result) -> Void in guard let range = result?.range(at: 1) else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.fontColor, range: range) if range.location == 0 { let listOpeningRegex = MarklightRegex(pattern: "([a-zA-Z_]+):", options: [.allowCommentsAndWhitespace]) listOpeningRegex.matches(string, range: range) { (result) -> Void in guard let range = result?.range(at: 0) else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.yamlOpenerColor, range: range) } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.yamlOpenerColor, range: NSRange(location: 0, length: 3)) attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.yamlOpenerColor, range: NSRange(location: range.length - 3, length: 3)) attributedString.addAttribute(NSAttributedString.Key.yamlBlock, value: range, range: range) } } // We detect and process underlined headers NotesTextProcessor.headersSetextRegex.matches(string, range: paragraphRange) { (result) -> Void in guard let range = result?.range else { return } attributedString.enumerateAttribute(.font, in: range) { value, subrange, _ in guard let font = value as? PlatformFont else { return } let headerFont = NotesTextProcessor.getHeaderFont(level: 1, baseFont: font, baseFontSize: pointSize) attributedString.addAttribute(.font, value: headerFont, range: subrange) } NotesTextProcessor.headersSetextUnderlineRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: innerRange) hideSyntaxIfNecessary(range: NSMakeRange(innerRange.location, innerRange.length)) } } // We detect and process dashed headers NotesTextProcessor.headersAtxRegex.matches(string, range: paragraphRange) { (result) -> Void in guard let range = result?.range, let headerMarksRange = result?.range(at: 1) else { return } let headerLevel = headerMarksRange.length attributedString.enumerateAttribute(.font, in: range) { value, subrange, _ in guard let font = value as? PlatformFont else { return } let headerFont = NotesTextProcessor.getHeaderFont(level: headerLevel, baseFont: font, baseFontSize: pointSize) attributedString.addAttribute(.font, value: headerFont, range: subrange) } NotesTextProcessor.headersAtxOpeningRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: innerRange) let syntaxRange = NSMakeRange(innerRange.location, innerRange.length + 1) hideSyntaxIfNecessary(range: syntaxRange) } NotesTextProcessor.headersAtxClosingRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: innerRange) hideSyntaxIfNecessary(range: innerRange) } } // We detect and process reference links NotesTextProcessor.referenceLinkRegex.matches(string, range: paragraphRange) { (result) -> Void in guard let range = result?.range else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: range) } // We detect and process lists NotesTextProcessor.listRegex.matches(string, range: paragraphRange) { (result) -> Void in guard let range = result?.range else { return } NotesTextProcessor.listOpeningRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: innerRange) } } #if IOS_APP || os(OSX) // We detect and process inline anchors (links) NotesTextProcessor.anchorInlineRegex.matches(string, range: paragraphRange) { (result) -> Void in guard let range = result?.range else { return } attributedString.addAttribute(.font, value: codeFont, range: range) //attributedString.fixAttributes(in: range) var destinationLink : String? NotesTextProcessor.coupleRoundRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: innerRange) guard let linkRange = result?.range(at: 3), linkRange.length > 0 else { return } let substring = attributedString.mutableString.substring(with: linkRange) guard substring.count > 0 else { return } destinationLink = substring attributedString.addAttribute(.link, value: substring, range: linkRange) hideSyntaxIfNecessary(range: innerRange) } NotesTextProcessor.openingSquareRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: innerRange) hideSyntaxIfNecessary(range: innerRange) } NotesTextProcessor.closingSquareRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: innerRange) hideSyntaxIfNecessary(range: innerRange) } guard let destinationLinkString = destinationLink else { return } NotesTextProcessor.coupleSquareRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } var _range = innerRange _range.location = _range.location + 1 _range.length = _range.length - 2 let substring = attributedString.mutableString.substring(with: _range) guard substring.lengthOfBytes(using: .utf8) > 0 else { return } attributedString.addAttribute(.link, value: destinationLinkString, range: _range) } } #endif NotesTextProcessor.anchorInlineGFMRegex.matches(string, range: paragraphRange) { (result) -> Void in guard let range = result?.range else { return } attributedString.addAttribute(.font, value: codeFont, range: range) //attributedString.fixAttributes(in: range) var destinationLink : String? NotesTextProcessor.coupleRoundRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: innerRange) if let linkRange = result?.range(at: 3), linkRange.length > 0 { let substring = attributedString.mutableString.substring(with: linkRange) guard substring.count > 0 else { return } destinationLink = substring attributedString.addAttribute(.link, value: substring, range: linkRange) let fullURL = attributedString.mutableString.substring(with: innerRange) if let angleStart = fullURL.range(of: "<")?.lowerBound, let angleEnd = fullURL.range(of: ">", options: .backwards)?.upperBound { let startOffset = fullURL.distance(from: fullURL.startIndex, to: angleStart) let endOffset = fullURL.distance(from: fullURL.startIndex, to: angleEnd) let openAngleRange = NSRange(location: innerRange.location + startOffset, length: 1) attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: openAngleRange) hideSyntaxIfNecessary(range: openAngleRange) let closeAngleRange = NSRange(location: innerRange.location + endOffset - 1, length: 1) attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: closeAngleRange) hideSyntaxIfNecessary(range: closeAngleRange) } } else if let linkRange = result?.range(at: 4), linkRange.length > 0 { let substring = attributedString.mutableString.substring(with: linkRange) guard substring.count > 0 else { return } destinationLink = substring attributedString.addAttribute(.link, value: substring, range: linkRange) } hideSyntaxIfNecessary(range: innerRange) } // Opening [ NotesTextProcessor.openingSquareRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: innerRange) hideSyntaxIfNecessary(range: innerRange) } // Closing ] NotesTextProcessor.closingSquareRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: innerRange) hideSyntaxIfNecessary(range: innerRange) } guard let destinationLinkString = destinationLink else { return } // Title [text] NotesTextProcessor.coupleSquareRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } var _range = innerRange _range.location = _range.location + 1 _range.length = _range.length - 2 let substring = attributedString.mutableString.substring(with: _range) guard substring.lengthOfBytes(using: .utf8) > 0 else { return } attributedString.addAttribute(.link, value: destinationLinkString, range: _range) } } // We detect and process app urls [[link]] NotesTextProcessor.appUrlRegex.matches(string, range: paragraphRange) { (result) -> Void in guard let innerRange = result?.range else { return } var _range = innerRange _range.location = _range.location + 2 _range.length = _range.length - 4 let appLink = attributedString.mutableString.substring(with: _range) guard !appLink.startsWith(string: "`") else { return } if let link = appLink.addingPercentEncoding(withAllowedCharacters: .alphanumerics) { #if os(iOS) attributedString.addAttribute(.foregroundColor, value: UIColor.wikiColor, range: innerRange) #endif attributedString.addAttribute(.link, value: "fsnotes://find?id=" + link, range: _range) if let range = result?.range(at: 0) { attributedString.addAttribute(.foregroundColor, value: Color.gray, range: range) } if let range = result?.range(at: 2) { attributedString.addAttribute(.foregroundColor, value: Color.gray, range: range) } } } // We detect and process quotes NotesTextProcessor.blockQuoteRegex.matches(string, range: paragraphRange) { (result) -> Void in guard let range = result?.range else { return } attributedString.addAttribute(.foregroundColor, value: quoteColor, range: range) NotesTextProcessor.blockQuoteOpeningRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: innerRange) hideSyntaxIfNecessary(range: innerRange) } } // We detect and process italics NotesTextProcessor.strictItalicRegex.matches(string, range: paragraphRange) { (result) -> Void in guard let range = result?.range(at: 3) else { return } if NotesTextProcessor.isLink(attributedString: attributedString, range: range) { return } addFontTraits([.italic], range: range, attributedString: attributedString) NotesTextProcessor.strictBoldRegex.matches(string, range: range) { (result) -> Void in guard let range = result?.range else { return } addFontTraits([.bold], range: range, attributedString: attributedString) } let preRange = NSMakeRange(range.location - 1, 1) attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: preRange) hideSyntaxIfNecessary(range: preRange) let postRange = NSMakeRange(range.location + range.length, 1) attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: postRange) hideSyntaxIfNecessary(range: postRange) } // We detect and process bolds NotesTextProcessor.strictBoldRegex.matches(string, range: paragraphRange) { (result) -> Void in guard let range = result?.range(at: 3) else { return } let boldString = attributedString.attributedSubstring(from: range) if boldString.string.contains("__") || boldString.string == "_" { return } if NotesTextProcessor.isLink(attributedString: attributedString, range: range) { return } if let font = boldString.attribute(.font, at: 0, effectiveRange: nil) as? Font, font.isItalic { } else { addFontTraits([.bold], range: range, attributedString: attributedString) NotesTextProcessor.strictItalicRegex.matches(string, range: range) { (result) -> Void in guard let range = result?.range else { return } addFontTraits([.italic], range: range, attributedString: attributedString) } } let preRange = NSMakeRange(range.location - 2, 2) attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: preRange) hideSyntaxIfNecessary(range: preRange) let postRange = NSMakeRange(range.location + range.length, 2) attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: postRange) hideSyntaxIfNecessary(range: postRange) } // NotesTextProcessor.italicRegex.matches(string, range: paragraphRange) { (result) -> Void in // guard let range = result?.range else { return } // addFontTraits([.italic], range: range, attributedString: attributedString) // // let preRange = NSMakeRange(range.location, 1) // attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: preRange) // // let postRange = NSMakeRange(range.location + range.length - 1, 1) // attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: postRange) // } // // NotesTextProcessor.boldRegex.matches(string, range: paragraphRange) { (result) -> Void in // guard let range = result?.range else { return } // addFontTraits([.bold], range: range, attributedString: attributedString) // // let preRange = NSMakeRange(range.location, 2) // attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: preRange) // // let postRange = NSMakeRange(range.location + range.length - 2, 2) // attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: postRange) // } // We detect and process bolds NotesTextProcessor.strikeRegex.matches(string, range: paragraphRange) { (result) -> Void in guard let range = result?.range else { return } attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.single.rawValue, range: NSRange(location: range.location + 2, length: range.length - 4)) //attributedString.fixAttributes(in: range) let preRange = NSMakeRange(range.location, 2) attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: preRange) hideSyntaxIfNecessary(range: preRange) let postRange = NSMakeRange(range.location + range.length - 2, 2) attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: postRange) hideSyntaxIfNecessary(range: postRange) } // We detect and process inline mailto links not formatted NotesTextProcessor.autolinkEmailRegex.matches(string, range: paragraphRange) { (result) -> Void in guard let range = result?.range else { return } let substring = attributedString.mutableString.substring(with: range) guard substring.lengthOfBytes(using: .utf8) > 0, URL(string: substring) != nil else { return } if substring.isValidEmail() { attributedString.addAttribute(.link, value: "mailto:\(substring)", range: range) } else { attributedString.addAttribute(.link, value: substring, range: range) } if NotesTextProcessor.hideSyntax { NotesTextProcessor.mailtoRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.font, value: hiddenFont, range: innerRange) attributedString.addAttribute(.foregroundColor, value: hiddenColor, range: innerRange) } } } // Inline tags if UserDefaultsManagement.inlineTags { FSParser.tagsInlineRegex.matches(string, range: paragraphRange) { (result) -> Void in guard var range = result?.range(at: 1) else { return } // Skip if indented code block let parRange = attributedString.mutableString.paragraphRange(for: range) let parString = attributedString.mutableString.substring(with: parRange) if parString.starts(with: " ") || parString.starts(with: "\t") { return } if NotesTextProcessor.getSpanCodeBlockRange(content: attributedString, range: range) != nil { return } if let ranges = codeBlockRanges { for range in ranges { if NSIntersectionRange(range, parRange).length > 0 { return } } } var substring = attributedString.mutableString.substring(with: range) guard !substring.isNumber && !substring.isHexColor() else { return } range = NSRange(location: range.location - 1, length: range.length + 1) substring = attributedString.mutableString.substring(with: range) .replacingOccurrences(of: "#", with: "") .replacingOccurrences(of: "\n", with: "") .trim() guard let tag = substring.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else { return } attributedString.addAttribute(.link, value: "fsnotes://open/?tag=\(tag)", range: range) attributedString.addAttribute(.tag, value: "\(tag)", range: range) } } attributedString.enumerateAttribute(.attachment, in: paragraphRange, options: []) { (value, range, stop) -> Void in if value != nil, let todo = attributedString.attribute(.todo, at: range.location, effectiveRange: nil) { let strikeRange = attributedString.mutableString.paragraphRange(for: range) attributedString.addAttribute(.strikethroughStyle, value: todo, range: strikeRange) } } guard UserDefaultsManagement.codeBlockHighlight else { return } // Code span removed attributedString.enumerateAttribute(.backgroundColor, in: paragraphRange) { (value, innerRange, _) in if value != nil { let font = UserDefaultsManagement.noteFont attributedString.removeAttribute(.backgroundColor, range: innerRange) attributedString.addAttribute(.font, value: font, range: innerRange) attributedString.fixAttributes(in: innerRange) } } NotesTextProcessor.codeSpanRegex.matches(string, range: paragraphRange) { (result) -> Void in guard let range = result?.range else { return } if attributedString.mutableString.substring(with: range).startsWith(string: "```") { return } attributedString.addAttribute(.font, value: codeFont, range: range) attributedString.fixAttributes(in: range) attributedString.addAttribute(.backgroundColor, value: NotesTextProcessor.codeSpanBackground, range: range) NotesTextProcessor.codeSpanOpeningRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: innerRange) } NotesTextProcessor.codeSpanClosingRegex.matches(string, range: range) { (innerResult) -> Void in guard let innerRange = innerResult?.range else { return } attributedString.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: innerRange) } } } public static func isLink(attributedString: NSAttributedString, range: NSRange) -> Bool { return attributedString.attributedSubstring(from: range).attribute(.link, at: 0, effectiveRange: nil) != nil } /// Tabs are automatically converted to spaces as part of the transform /// this constant determines how "wide" those tabs become in spaces public static let _tabWidth = 4 // MARK: Headers /* Head ====== Subhead ------- */ fileprivate static let headerSetextPattern = [ "^(.+?)", "\\p{Z}*", "\\n", "(==+)", // $1 = string of ='s or -'s "\\p{Z}*", "\\n|\\Z" ].joined(separator: "\n") public static let headersSetextRegex = MarklightRegex(pattern: headerSetextPattern, options: [.allowCommentsAndWhitespace, .anchorsMatchLines]) fileprivate static let setextUnderlinePattern = [ "(==+|--+) # $1 = string of ='s or -'s", "\\p{Z}*$" ].joined(separator: "\n") public static let headersSetextUnderlineRegex = MarklightRegex(pattern: setextUnderlinePattern, options: [.allowCommentsAndWhitespace]) /* # Head ## Subhead ## */ fileprivate static let headerAtxPattern = [ "^(\\#{1,6}\\ ) # $1 = string of #'s", "\\p{Z}*", "(.+?) # $2 = Header text", "\\p{Z}*", "\\#* # optional closing #'s (not counted)", "(?:\\n|\\Z)" ].joined(separator: "\n") public static let headersAtxRegex = MarklightRegex(pattern: headerAtxPattern, options: [.allowCommentsAndWhitespace, .anchorsMatchLines]) fileprivate static let headersAtxOpeningPattern = [ "^(\\#{1,6}\\ )" ].joined(separator: "\n") public static let headersAtxOpeningRegex = MarklightRegex(pattern: headersAtxOpeningPattern, options: [.allowCommentsAndWhitespace, .anchorsMatchLines]) fileprivate static let headersAtxClosingPattern = [ "\\#{1,6}\\ \\n+" ].joined(separator: "\n") public static let headersAtxClosingRegex = MarklightRegex(pattern: headersAtxClosingPattern, options: [.allowCommentsAndWhitespace, .anchorsMatchLines]) // MARK: Reference links /* TODO: we don't know how reference links are formed */ fileprivate static let referenceLinkPattern = [ "^\\p{Z}{0,\(_tabWidth - 1)}\\[([^\\[\\]]+)\\]: # id = $1", " \\p{Z}*", " \\n? # maybe *one* newline", " \\p{Z}*", "? # url = $2", " \\p{Z}*", " \\n? # maybe one newline", " \\p{Z}*", "(?:", " (?<=\\s) # lookbehind for whitespace", " [\"(]", " (.+?) # title = $3", " [\")]", " \\p{Z}*", ")? # title is optional", "(?:\\n|\\Z)" ].joined(separator: "") public static let referenceLinkRegex = MarklightRegex(pattern: referenceLinkPattern, options: [.allowCommentsAndWhitespace, .anchorsMatchLines]) // MARK: Lists /* * First element * Second element */ fileprivate static let _markerUL = "[*+-]" fileprivate static let _markerOL = "[0-9-]+[.]" fileprivate static let _listMarker = "(?:\\p{Z}|\\t)*(?:\(_markerUL)|\(_markerOL))" fileprivate static let _listSingleLinePattern = "^(?:\\p{Z}|\\t)*((?:[*+-]|\\d+[.]))\\p{Z}+" public static let listRegex = MarklightRegex(pattern: _listSingleLinePattern, options: [.allowCommentsAndWhitespace, .anchorsMatchLines]) public static let listOpeningRegex = MarklightRegex(pattern: _listMarker, options: [.allowCommentsAndWhitespace]) // MARK: Anchors /* [Title](http://example.com) */ fileprivate static let anchorPattern = [ "( # wrap whole match in $1", " \\[", " (\(NotesTextProcessor.getNestedBracketsPattern())) # link text = $2", " \\]", "", " \\p{Z}? # one optional space", " (?:\\n\\p{Z}*)? # one optional newline followed by spaces", "", " \\[", " (.*?) # id = $3", " \\]", ")" ].joined(separator: "\n") public static let anchorRegex = MarklightRegex(pattern: anchorPattern, options: [.allowCommentsAndWhitespace, .dotMatchesLineSeparators]) fileprivate static let opneningSquarePattern = [ "(\\[)" ].joined(separator: "\n") public static let openingSquareRegex = MarklightRegex(pattern: opneningSquarePattern, options: [.allowCommentsAndWhitespace]) fileprivate static let closingSquarePattern = [ "\\]" ].joined(separator: "\n") public static let closingSquareRegex = MarklightRegex(pattern: closingSquarePattern, options: [.allowCommentsAndWhitespace]) fileprivate static let coupleSquarePattern = [ "\\[(.*?)\\]" ].joined(separator: "\n") public static let coupleSquareRegex = MarklightRegex(pattern: coupleSquarePattern, options: []) fileprivate static let coupleRoundPattern = [ ".*(?:\\])\\((.+)\\)" ].joined(separator: "\n") public static let coupleRoundRegex = MarklightRegex(pattern: coupleRoundPattern, options: []) fileprivate static let parenPattern = [ "(", "\\( # literal paren", " \\p{Z}*", " (\(NotesTextProcessor.getNestedParensPattern())) # href = $3", " \\p{Z}*", " ( # $4", " (['\"]) # quote char = $5", " (.*?) # title = $6", " \\5 # matching quote", " \\p{Z}*", " )? # title is optional", " \\)", ")" ].joined(separator: "\n") public static let parenRegex = MarklightRegex(pattern: parenPattern, options: [.allowCommentsAndWhitespace]) fileprivate static let anchorInlinePattern = [ "( # wrap whole match in $1", " \\[", " (\(NotesTextProcessor.getNestedBracketsPattern())) # link text = $2", " \\]", " \\( # literal paren", " \\p{Z}*", " (\(NotesTextProcessor.getNestedParensPattern())) # href = $3", " \\p{Z}*", " ( # $4", " (['\"]) # quote char = $5", " (.*?) # title = $6", " \\5 # matching quote", " \\p{Z}* # ignore any spaces between closing quote and )", " )? # title is optional", " \\)", ")" ].joined(separator: "\n") public static let anchorInlineRegex = MarklightRegex(pattern: anchorInlinePattern, options: [.allowCommentsAndWhitespace, .dotMatchesLineSeparators]) // Mark: GFM links fileprivate static let anchorInlineGFMPattern = [ "( # wrap whole match in $1", " \\[", " (\(NotesTextProcessor.getNestedBracketsPattern())) # link text = $2", " \\]", " \\( # literal paren", " \\p{Z}*", " (?: # URL group (non-capturing)", " < # opening angle bracket", " ([^>]+) # href with spaces = $3", " > # closing angle bracket", " | # OR", " (\(NotesTextProcessor.getNestedParensPattern())) # regular href = $4", " )", " \\p{Z}*", " ( # $5", " (['\"]) # quote char = $6", " (.*?) # title = $7", " \\6 # matching quote", " \\p{Z}* # ignore any spaces between closing quote and )", " )? # title is optional", " \\)", ")" ].joined(separator: "\n") public static let anchorInlineGFMRegex = MarklightRegex(pattern: anchorInlineGFMPattern, options: [.allowCommentsAndWhitespace, .dotMatchesLineSeparators]) // Mark: Images /* ![Title](http://example.com/image.png) */ fileprivate static let imagePattern = [ "( # wrap whole match in $1", "!\\[", " (.*?) # alt text = $2", "\\]", "", "\\p{Z}? # one optional space", "(?:\\n\\p{Z}*)? # one optional newline followed by spaces", "", "\\[", " (.*?) # id = $3", "\\]", "", ")" ].joined(separator: "\n") public static let imageRegex = MarklightRegex(pattern: imagePattern, options: [.allowCommentsAndWhitespace, .dotMatchesLineSeparators]) fileprivate static let imageOpeningSquarePattern = [ "(!\\[)" ].joined(separator: "\n") public static let imageOpeningSquareRegex = MarklightRegex(pattern: imageOpeningSquarePattern, options: [.allowCommentsAndWhitespace]) fileprivate static let imageClosingSquarePattern = [ "(\\])" ].joined(separator: "\n") public static let imageClosingSquareRegex = MarklightRegex(pattern: imageClosingSquarePattern, options: [.allowCommentsAndWhitespace]) fileprivate static let todoInlinePattern = "(^(-\\ \\[(?:\\ |x)\\])\\ )" public static let todoInlineRegex = MarklightRegex(pattern: todoInlinePattern, options: [.allowCommentsAndWhitespace, .anchorsMatchLines]) fileprivate static let allTodoInlinePattern = "((-\\ \\[(?:\\ |x)\\])\\ )" public static let allTodoInlineRegex = MarklightRegex(pattern: allTodoInlinePattern, options: [.allowCommentsAndWhitespace, .anchorsMatchLines]) fileprivate static let codeSpanPattern = [ "(? Quoted text */ fileprivate static let blockQuotePattern = [ "( # Wrap whole match in $1", " (", " ^\\p{Z}*>\\p{Z}? # '>' at the start of a line", " .+(?:\\n|\\Z) # rest of the first line", " (.+(?:\\n|\\Z))* # subsequent consecutive lines", " (?:\\n|\\Z)* # blanks", " )+", ")" ].joined(separator: "\n") public static let blockQuoteRegex = MarklightRegex(pattern: blockQuotePattern, options: [.allowCommentsAndWhitespace, .anchorsMatchLines]) fileprivate static let blockQuoteOpeningPattern = [ "(^\\p{Z}*>\\p{Z})" ].joined(separator: "\n") public static let blockQuoteOpeningRegex = MarklightRegex(pattern: blockQuoteOpeningPattern, options: [.anchorsMatchLines]) // MARK: App url fileprivate static let appUrlPattern = "(\\[\\[)(.+?[\\[\\]]*)(\\]\\])" public static let appUrlRegex = MarklightRegex(pattern: appUrlPattern, options: [.anchorsMatchLines]) // MARK: Bold /* **Bold** __Bold__ */ fileprivate static let strictBoldPattern = "(^|[\\W_])(?:(?!\\1)|(?=^))(\\*|_)\\2(?=\\S)(.*?\\S)\\2\\2(?!\\2)(?=[\\W_]|$)" public static let strictBoldRegex = MarklightRegex(pattern: strictBoldPattern, options: [.anchorsMatchLines]) fileprivate static let boldPattern = "(\\*\\*) (?=\\S) (.+?[*_]*) (?<=\\S) \\1" public static let boldRegex = MarklightRegex(pattern: boldPattern, options: [.allowCommentsAndWhitespace, .anchorsMatchLines]) fileprivate static let strikePattern = "(\\~\\~) (?=\\S) (.+?[~]*) (?<=\\S) \\1" public static let strikeRegex = MarklightRegex(pattern: strikePattern, options: [.allowCommentsAndWhitespace, .anchorsMatchLines]) // MARK: Italic /* *Italic* _Italic_ */ fileprivate static let strictItalicPattern = "(^|[\\W_])(?:(?!\\1)|(?=^))(\\*|_)(?=\\S)((?:(?!\\2).)*?\\S)\\2(?!\\2)(?=[\\W_]|$)" public static let strictItalicRegex = MarklightRegex(pattern: strictItalicPattern, options: [.anchorsMatchLines]) fileprivate static let italicPattern = "(\\*) (?=\\S) (.+?) (?<=\\S) \\1" public static let italicRegex = MarklightRegex(pattern: italicPattern, options: [.allowCommentsAndWhitespace, .anchorsMatchLines]) fileprivate static let autolinkPattern = "([\\(]*(https?|sftp|file|ftp):[^`\'\">\\s\\*]+)" public static let autolinkRegex = MarklightRegex(pattern: autolinkPattern, options: [.allowCommentsAndWhitespace, .dotMatchesLineSeparators]) fileprivate static let autolinkPrefixPattern = "((https?|sftp|file|ftp)://)" public static let autolinkPrefixRegex = MarklightRegex(pattern: autolinkPrefixPattern, options: [.allowCommentsAndWhitespace, .dotMatchesLineSeparators]) fileprivate static let autolinkEmailPattern = [ "(?:mailto:)?", "(", " [-.\\w]+", " \\@", " [-a-z0-9]+(\\.[-a-z0-9]+)*\\.[a-z]+", ")" ].joined(separator: "\n") public static let autolinkEmailRegex = MarklightRegex(pattern: autolinkEmailPattern, options: [.allowCommentsAndWhitespace, .dotMatchesLineSeparators]) fileprivate static let mailtoPattern = "mailto:" public static let mailtoRegex = MarklightRegex(pattern: mailtoPattern, options: [.allowCommentsAndWhitespace, .dotMatchesLineSeparators]) /// maximum nested depth of [] and () supported by the transform; /// implementation detail fileprivate static let _nestDepth = 6 fileprivate static var _nestedBracketsPattern = "" fileprivate static var _nestedParensPattern = "" /// Reusable pattern to match balanced [brackets]. See Friedl's /// "Mastering Regular Expressions", 2nd Ed., pp. 328-331. fileprivate static func getNestedBracketsPattern() -> String { // in other words [this] and [this[also]] and [this[also[too]]] // up to _nestDepth if (_nestedBracketsPattern.isEmpty) { _nestedBracketsPattern = repeatString([ "(?> # Atomic matching", "[^\\[\\]]+ # Anything other than brackets", "|", "\\[" ].joined(separator: "\n"), _nestDepth) + repeatString(" \\])*", _nestDepth) } return _nestedBracketsPattern } /// Reusable pattern to match balanced (parens). See Friedl's /// "Mastering Regular Expressions", 2nd Ed., pp. 328-331. fileprivate static func getNestedParensPattern() -> String { // in other words (this) and (this(also)) and (this(also(too))) // up to _nestDepth if (_nestedParensPattern.isEmpty) { _nestedParensPattern = repeatString([ "(?> # Atomic matching", "[^()\\s]+ # Anything other than parens or whitespace", "|", "\\(" ].joined(separator: "\n"), _nestDepth) + repeatString(" \\))*", _nestDepth) } return _nestedParensPattern } /// this is to emulate what's available in PHP fileprivate static func repeatString(_ text: String, _ count: Int) -> String { return Array(repeating: text, count: count).reduce("", +) } fileprivate static func getHeaderFont(level: Int, baseFont: PlatformFont, baseFontSize: CGFloat) -> PlatformFont { let headerSize: CGFloat switch level { case 1: headerSize = baseFontSize * 2.0 // # case 2: headerSize = baseFontSize * 1.7 // ## case 3: headerSize = baseFontSize * 1.4 // ### case 4: headerSize = baseFontSize * 1.2 // #### case 5: headerSize = baseFontSize * 1.1 // ##### case 6: headerSize = baseFontSize * 1.05 // ###### default: headerSize = baseFontSize } let boldTraits: FontTraits = [.bold] var fontDescriptor = baseFont.fontDescriptor .withSymbolicTraits(boldTraits) #if os(OSX) fontDescriptor = fontDescriptor.withSize(headerSize) return PlatformFont(descriptor: fontDescriptor, size: headerSize) ?? baseFont #else fontDescriptor = fontDescriptor?.withSize(headerSize) guard let fontDescriptor = fontDescriptor else { return baseFont } return PlatformFont(descriptor: fontDescriptor, size: headerSize) #endif } } public struct MarklightRegex { public let regularExpression: NSRegularExpression! public init(pattern: String, options: NSRegularExpression.Options = NSRegularExpression.Options(rawValue: 0)) { var error: NSError? let re: NSRegularExpression? do { re = try NSRegularExpression(pattern: pattern, options: options) } catch let error1 as NSError { error = error1 re = nil } // If re is nil, it means NSRegularExpression didn't like // the pattern we gave it. All regex patterns used by Markdown // should be valid, so this probably means that a pattern // valid for .NET Regex is not valid for NSRegularExpression. if re == nil { if let error = error { print("Regular expression error: \(error.userInfo)") } assert(re != nil) } self.regularExpression = re } public func matches(_ input: String, range: NSRange, completion: @escaping (_ result: NSTextCheckingResult?) -> Void) { let s = input as NSString //NSRegularExpression. let options = NSRegularExpression.MatchingOptions(rawValue: 0) regularExpression.enumerateMatches(in: s as String, options: options, range: range, using: { (result, flags, stop) -> Void in completion(result) }) } } ================================================ FILE: FSNotesCore/RepositoryAction.swift ================================================ // // RepositoryAction.swift // FSNotes // // Created by Oleksandr Hlushchenko on 13.03.2023. // Copyright © 2023 Oleksandr Hlushchenko. All rights reserved. // import Foundation public enum RepositoryAction: Int, CaseIterable { case initCommit case clonePush case commit case pullPush var title: String { switch self { case .initCommit: return "Init/commit" case .clonePush: return "Clone/push" case .pullPush: return "Pull/push" case .commit: return "Add/commit" } } } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Assembly.swift ================================================ // // AssemblyLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct AssemblyLanguage: LanguageDefinition { let name = "Assembly" let aliases: [String]? = ["asm", "asm86", "nasm", "masm", "gas"] let caseInsensitive = true let keywords: [String: [String]]? = [ "keyword": [ // Data movement "mov", "movb", "movw", "movl", "movq", "movsx", "movzx", "lea", "xchg", "push", "pop", "pusha", "popa", "pushad", "popad", "pushf", "popf", "pushfd", "popfd", // Arithmetic "add", "adc", "sub", "sbb", "mul", "imul", "div", "idiv", "inc", "dec", "neg", "cmp", "aaa", "aas", "aam", "aad", "daa", "das", // Logical "and", "or", "xor", "not", "test", "shl", "shr", "sal", "sar", "rol", "ror", "rcl", "rcr", "shld", "shrd", // Control flow "jmp", "je", "jz", "jne", "jnz", "jg", "jge", "jl", "jle", "ja", "jae", "jb", "jbe", "js", "jns", "jo", "jno", "jp", "jpe", "jnp", "jpo", "jcxz", "jecxz", "jrcxz", "call", "ret", "retn", "retf", "iret", "iretd", "iretq", "loop", "loope", "loopz", "loopne", "loopnz", // String operations "movs", "movsb", "movsw", "movsd", "movsq", "cmps", "cmpsb", "cmpsw", "cmpsd", "cmpsq", "scas", "scasb", "scasw", "scasd", "scasq", "lods", "lodsb", "lodsw", "lodsd", "lodsq", "stos", "stosb", "stosw", "stosd", "stosq", "rep", "repe", "repz", "repne", "repnz", // Stack frame "enter", "leave", // Flag operations "clc", "stc", "cmc", "cld", "std", "cli", "sti", "lahf", "sahf", "pushf", "popf", "pushfd", "popfd", // Processor control "nop", "hlt", "wait", "lock", "esc", // Set byte on condition "sete", "setz", "setne", "setnz", "setg", "setge", "setl", "setle", "seta", "setae", "setb", "setbe", "sets", "setns", "seto", "setno", "setp", "setpe", "setnp", "setpo", // Conditional move "cmove", "cmovz", "cmovne", "cmovnz", "cmovg", "cmovge", "cmovl", "cmovle", "cmova", "cmovae", "cmovb", "cmovbe", "cmovs", "cmovns", "cmovo", "cmovno", "cmovc", "cmovnc", // Bit manipulation "bt", "btc", "btr", "bts", "bsf", "bsr", "bswap", // I/O "in", "out", "ins", "insb", "insw", "insd", "outs", "outsb", "outsw", "outsd", // System "int", "into", "bound", "cpuid", "rdtsc", "rdmsr", "wrmsr", "lgdt", "sgdt", "lidt", "sidt", "lldt", "sldt", "ltr", "str", "lmsw", "smsw", "clts", "arpl", "lar", "lsl", "verr", "verw", "invd", "wbinvd", "invlpg", "invpcid", // x87 FPU "fld", "fst", "fstp", "fild", "fist", "fistp", "fbld", "fbstp", "fxch", "fcmove", "fcmovne", "fcmovb", "fcmovbe", "fcmovnb", "fcmovnbe", "fadd", "faddp", "fiadd", "fsub", "fsubp", "fisub", "fsubr", "fsubrp", "fisubr", "fmul", "fmulp", "fimul", "fdiv", "fdivp", "fidiv", "fdivr", "fdivrp", "fidivr", "fabs", "fchs", "fcom", "fcomp", "fcompp", "ficom", "ficomp", "fcomi", "fcomip", "fucomi", "fucomip", "ftst", "fxam", "fsqrt", "fsin", "fcos", "fsincos", "fptan", "fpatan", "f2xm1", "fyl2x", "fyl2xp1", "fldz", "fld1", "fldpi", "fldl2e", "fldl2t", "fldlg2", "fldln2", "finit", "fninit", "fclex", "fnclex", "fstcw", "fnstcw", "fldcw", "fstenv", "fnstenv", "fldenv", "fsave", "fnsave", "frstor", "fincstp", "fdecstp", "ffree", "ffreep", "fnop", "fwait", // SSE/AVX "movaps", "movups", "movss", "movsd", "movdqa", "movdqu", "movq", "addps", "addss", "subps", "subss", "mulps", "mulss", "divps", "divss", "sqrtps", "sqrtss", "maxps", "maxss", "minps", "minss", "andps", "andnps", "orps", "xorps", "cmpps", "cmpss", "vmovaps", "vmovups", "vaddps", "vsubps", "vmulps", "vdivps", // MMX "movd", "movq", "packsswb", "packssdw", "packuswb", "paddb", "paddw", "paddd", "paddsb", "paddsw", "paddusb", "paddusw", "pand", "pandn", "por", "pxor", "pcmpeqb", "pcmpeqw", "pcmpeqd", "pcmpgtb", "pcmpgtw", "pcmpgtd", "pmaddwd", "pmulhw", "pmullw", "psllw", "pslld", "psllq", "psraw", "psrad", "psrlw", "psrld", "psrlq", "psubb", "psubw", "psubd", "psubsb", "psubsw", "psubusb", "psubusw", "punpckhbw", "punpckhwd", "punpckhdq", "punpcklbw", "punpcklwd", "punpckldq", "emms" ], "literal": [], "built_in": [ // Registers - 8-bit "al", "ah", "bl", "bh", "cl", "ch", "dl", "dh", "spl", "bpl", "sil", "dil", "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b", // Registers - 16-bit "ax", "bx", "cx", "dx", "si", "di", "bp", "sp", "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w", "ip", "cs", "ds", "es", "fs", "gs", "ss", // Registers - 32-bit "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp", "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d", "eip", "eflags", // Registers - 64-bit "rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "rsp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "rip", "rflags", // FPU registers "st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7", "st", // MMX registers "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", // XMM registers (SSE) "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", // YMM registers (AVX) "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15", // ZMM registers (AVX-512) "zmm0", "zmm1", "zmm2", "zmm3", "zmm4", "zmm5", "zmm6", "zmm7", "zmm8", "zmm9", "zmm10", "zmm11", "zmm12", "zmm13", "zmm14", "zmm15", "zmm16", "zmm17", "zmm18", "zmm19", "zmm20", "zmm21", "zmm22", "zmm23", "zmm24", "zmm25", "zmm26", "zmm27", "zmm28", "zmm29", "zmm30", "zmm31", // Control registers "cr0", "cr2", "cr3", "cr4", "cr8", // Debug registers "dr0", "dr1", "dr2", "dr3", "dr6", "dr7", // Size directives "byte", "word", "dword", "qword", "tbyte", "oword", "yword", "zword", "ptr", "offset", "seg", // Data types "db", "dw", "dd", "dq", "dt", "do", "dy", "dz", "resb", "resw", "resd", "resq", "rest", "reso", "resy", "resz", // Directives "section", "segment", "global", "extern", "public", "extrn", "align", "alignb", "bits", "use16", "use32", "use64", "org", "times", "equ", "macro", "endm", "struc", "endstruc", "istruc", "iend", "end", "proc", "endp", // Special "short", "near", "far", "abs", "rel" ] ] let contains: [Mode] = [ Mode(scope: "comment", begin: "/\\*", end: "\\*/"), Mode(scope: "comment", begin: ";", end: "\n"), Mode(scope: "comment", begin: "#", end: "\n"), Mode(scope: "comment", begin: "//", end: "\n"), Mode(scope: "comment", begin: "@", end: "\n"), Mode(scope: "meta", begin: "^\\s*%(?:define|undef|include|ifdef|ifndef|if|elif|else|endif|macro|endmacro|rep|endrep)\\b"), Mode(scope: "meta", begin: "^\\.(?:text|data|bss|section|global|extern|align|ascii|asciz|byte|word|long|quad)\\b"), Mode(scope: "meta", begin: "^\\s*\\.\\w+"), Mode(scope: "function", begin: "^[a-zA-Z_][a-zA-Z0-9_]*:"), Mode(scope: "function", begin: "^\\.[a-zA-Z_][a-zA-Z0-9_]*:"), Mode(scope: "function", begin: "^\\d+:"), CommonModes.stringDouble, CommonModes.stringSingle, Mode(scope: "string", begin: "'(?:[^'\\\\]|\\\\.)'"), // Binary Mode(scope: "number", begin: "\\b0[bB][01]+[hH]?\\b"), Mode(scope: "number", begin: "\\b[01]+[bB]\\b"), // Octal Mode(scope: "number", begin: "\\b0[oO][0-7]+\\b"), Mode(scope: "number", begin: "\\b[0-7]+[oOqQ]\\b"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+\\b"), Mode(scope: "number", begin: "\\b[0-9][0-9a-fA-F]*[hH]\\b"), Mode(scope: "number", begin: "\\$[0-9a-fA-F]+\\b"), // Decimal Mode(scope: "number", begin: "\\b\\d+[dD]?\\b"), // Float Mode(scope: "number", begin: "\\b\\d+\\.\\d+(?:[eE][+-]?\\d+)?\\b"), Mode(scope: "meta", begin: "\\[", end: "\\]"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Bash.swift ================================================ // // BashLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct BashLanguage: LanguageDefinition { let name = "Bash" let aliases: [String]? = ["bash", "sh", "shell", "zsh"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ // Control flow "if", "then", "else", "elif", "fi", "case", "esac", "for", "select", "while", "until", "do", "done", "in", "function", "time", // Declarations "declare", "typeset", "local", "export", "readonly", "unset", // Built-in commands "break", "continue", "return", "exit", "shift", "eval", "exec", "source", ".", "trap", "wait", "jobs", "bg", "fg", "disown", "suspend", "alias", "unalias", "set", "unset", "shopt", "enable", "command", "builtin", "caller", "true", "false", // Test commands "test", "[", "[[", // Compound commands "{", "}", "((", "))", "[[", "]]" ], "literal": ["true", "false"], "built_in": [ // File operations "cat", "cp", "mv", "rm", "rmdir", "mkdir", "touch", "ln", "chmod", "chown", "chgrp", "ls", "pwd", "cd", "pushd", "popd", "dirs", "find", "locate", "which", "whereis", "file", "stat", "du", "df", "mount", "umount", "dd", "tar", "gzip", "gunzip", "bzip2", "bunzip2", "zip", "unzip", "compress", "uncompress", "rsync", "scp", "sftp", // Text processing "echo", "printf", "read", "cat", "head", "tail", "less", "more", "grep", "egrep", "fgrep", "sed", "awk", "cut", "paste", "join", "sort", "uniq", "wc", "tr", "expand", "unexpand", "fold", "fmt", "nl", "pr", "tee", "split", "csplit", "diff", "patch", "cmp", "comm", "column", "iconv", "dos2unix", "unix2dos", // Process management "ps", "top", "htop", "kill", "killall", "pkill", "pgrep", "pidof", "nice", "renice", "nohup", "screen", "tmux", "at", "batch", "cron", "crontab", "sleep", "timeout", "watch", "xargs", // System information "uname", "hostname", "uptime", "who", "whoami", "id", "groups", "users", "last", "lastlog", "w", "finger", "date", "cal", "time", "timedatectl", "localectl", "hostnamectl", // Network "ping", "traceroute", "netstat", "ss", "ip", "ifconfig", "route", "arp", "dig", "nslookup", "host", "wget", "curl", "nc", "netcat", "telnet", "ftp", "ssh", "scp", "rsync", "nmap", "tcpdump", // User management "useradd", "usermod", "userdel", "groupadd", "groupmod", "groupdel", "passwd", "chpasswd", "su", "sudo", "visudo", // Package management "apt", "apt-get", "aptitude", "dpkg", "yum", "dnf", "rpm", "zypper", "pacman", "brew", "snap", "flatpak", // System management "systemctl", "service", "journalctl", "dmesg", "shutdown", "reboot", "poweroff", "halt", "init", "telinit", // Shell built-ins "alias", "bg", "bind", "builtin", "caller", "cd", "command", "compgen", "complete", "compopt", "continue", "declare", "dirs", "disown", "echo", "enable", "eval", "exec", "exit", "export", "false", "fc", "fg", "getopts", "hash", "help", "history", "jobs", "kill", "let", "local", "logout", "mapfile", "popd", "printf", "pushd", "pwd", "read", "readarray", "readonly", "return", "set", "shift", "shopt", "source", "suspend", "test", "times", "trap", "true", "type", "typeset", "ulimit", "umask", "unalias", "unset", "wait", // Common utilities "basename", "dirname", "expr", "bc", "dc", "seq", "yes", "tty", "stty", "clear", "reset", "script", "rev", "factor", "env", "printenv", "getopt", "getopts", "mktemp", "mkfifo", "tput", // Archiving "tar", "cpio", "zip", "unzip", "gzip", "gunzip", "bzip2", "bunzip2", "xz", "unxz", "7z", "rar", "unrar", // Disk operations "fdisk", "parted", "mkfs", "fsck", "tune2fs", "resize2fs", "blkid", "lsblk", "hdparm", "smartctl", // Variables "PATH", "HOME", "USER", "SHELL", "PWD", "OLDPWD", "TMPDIR", "LANG", "LC_ALL", "TERM", "EDITOR", "VISUAL", "PAGER", "PS1", "PS2", "PS3", "PS4", "IFS", "RANDOM", "SECONDS", "LINENO", "BASHPID", "BASH_VERSION", "HOSTNAME", "UID", "EUID", "GROUPS", "PPID", "SHLVL", "BASH_SUBSHELL", // Special parameters "$@", "$*", "$#", "$$", "$!", "$?", "$-", "$_", "$0", // Test operators "-e", "-f", "-d", "-L", "-h", "-b", "-c", "-p", "-S", "-t", "-r", "-w", "-x", "-s", "-u", "-g", "-k", "-O", "-G", "-N", "-nt", "-ot", "-ef", "-z", "-n", "=", "!=", "==", "-eq", "-ne", "-lt", "-le", "-gt", "-ge", "&&", "||", "!" ] ] let contains: [Mode] = [ // Shebang Mode(scope: "comment", begin: "^#!", end: "\n"), Mode(scope: "comment", begin: "#", end: "\n"), // Heredoc Mode(scope: "string", begin: "<<-?\\s*(['\"]?)([a-zA-Z_][a-zA-Z0-9_]*)\\1", end: "^\\2$"), // Variables Mode(scope: "meta", begin: "\\$[a-zA-Z_][a-zA-Z0-9_]*"), Mode(scope: "meta", begin: "\\$\\{[^}]+\\}"), Mode(scope: "meta", begin: "\\$\\([^)]+\\)"), Mode(scope: "meta", begin: "\\$\\(\\([^)]+\\)\\)"), // Special Mode(scope: "meta", begin: "\\$[0-9@*#?$!_-]"), // Command substitution (backticks) Mode(scope: "string", begin: "`", end: "`"), // Strings with double quotes (allows variable expansion) Mode(scope: "string", begin: "\"", end: "\""), // Strings with single quotes (no expansion) CommonModes.stringSingle, // ANSI-C quoting Mode(scope: "string", begin: "\\$'", end: "'"), // Functions Mode(scope: "function", begin: "^\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(\\s*\\)"), Mode(scope: "function", begin: "\\bfunction\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), // Numbers Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+\\b"), Mode(scope: "number", begin: "\\b0[0-7]+\\b"), Mode(scope: "number", begin: "\\b[0-9]+\\b"), // Redirection operators Mode(scope: "keyword", begin: "[0-9]*(?:>>|>|<<|<|&>|&>>|<&|>&|<>)"), // Pipe Mode(scope: "keyword", begin: "\\|\\|?|&&?|;|&"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/C.swift ================================================ // // CLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct CLanguage: LanguageDefinition { let name = "C" let aliases: [String]? = ["c", "h"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", // C99 "_Bool", "_Complex", "_Imaginary", // C11 "_Alignas", "_Alignof", "_Atomic", "_Generic", "_Noreturn", "_Static_assert", "_Thread_local" ], "literal": ["true", "false", "NULL"], "built_in": [ // stdio.h "printf", "scanf", "fprintf", "fscanf", "sprintf", "sscanf", "fopen", "fclose", "fread", "fwrite", "fgets", "fputs", "fgetc", "fputc", "getchar", "putchar", "puts", "gets", "fseek", "ftell", "rewind", "feof", "ferror", // stdlib.h "malloc", "calloc", "realloc", "free", "exit", "abort", "atexit", "atoi", "atof", "atol", "strtol", "strtod", "rand", "srand", "abs", "labs", "div", "ldiv", "qsort", "bsearch", // string.h "strlen", "strcpy", "strncpy", "strcat", "strncat", "strcmp", "strncmp", "strchr", "strrchr", "strstr", "strtok", "memcpy", "memmove", "memset", "memcmp", "memchr", // math.h "sin", "cos", "tan", "asin", "acos", "atan", "atan2", "sinh", "cosh", "tanh", "exp", "log", "log10", "pow", "sqrt", "ceil", "floor", "fabs", "fmod", // time.h "time", "clock", "difftime", "mktime", "strftime", "gmtime", "localtime", // ctype.h "isalnum", "isalpha", "isdigit", "islower", "isupper", "isspace", "toupper", "tolower", // assert.h "assert", // Типы "size_t", "ptrdiff_t", "wchar_t", "FILE", "time_t", "clock_t" ] ] let contains: [Mode] = [ Mode(scope: "meta", begin: "^\\s*#\\s*(?:include|define|undef|if|ifdef|ifndef|else|elif|endif|error|pragma|line)\\b.*$"), Mode(scope: "comment", begin: "//", end: "\n"), Mode(scope: "comment", begin: "/\\*", end: "\\*/"), Mode(scope: "function", begin: "\\b[a-zA-Z_][a-zA-Z0-9_]*\\s*(?=\\()"), CommonModes.stringDouble, Mode(scope: "string", begin: "'(?:[^'\\\\]|\\\\.)+'"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+[uUlL]*\\b"), // Octal Mode(scope: "number", begin: "\\b0[0-7]+[uUlL]*\\b"), // Float/Double Mode(scope: "number", begin: "\\b\\d+\\.\\d+[fFlL]?\\b"), Mode(scope: "number", begin: "\\b\\d+[eE][+-]?\\d+[fFlL]?\\b"), Mode(scope: "number", begin: "\\b\\d+\\.\\d+[eE][+-]?\\d+[fFlL]?\\b"), // Integer Mode(scope: "number", begin: "\\b\\d+[uUlL]*\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Clojure.swift ================================================ // // Clojure.swift // FSNotes // // Created by Oleksandr Hlushchenko on 09.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct ClojureLanguage: LanguageDefinition { let name = "Clojure" let aliases: [String]? = ["clojure", "clj", "cljs", "cljc", "edn"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ // Special forms "def", "if", "do", "let", "quote", "var", "fn", "loop", "recur", "throw", "try", "catch", "finally", "monitor-enter", "monitor-exit", // Defining forms "defn", "defn-", "defmacro", "defmethod", "defmulti", "defonce", "defprotocol", "defrecord", "defstruct", "deftype", "definterface", // Binding "let", "letfn", "binding", "with-bindings", "with-bindings*", "with-local-vars", "with-open", "with-precision", "with-redefs", "with-redefs-fn", // Conditionals "if", "if-not", "if-let", "if-some", "when", "when-not", "when-let", "when-some", "when-first", "cond", "condp", "case", // Loops "loop", "recur", "while", "dotimes", "doseq", "for", "doto", // Functions "fn", "defn", "defn-", "defmacro", "comp", "partial", "constantly", "identity", "complement", "juxt", "some-fn", "every-pred", // Java interop "new", ".", "..", "set!", "import", "gen-class", "gen-interface", "proxy", "proxy-super", "reify", "memfn", "bean", // Namespace "ns", "in-ns", "create-ns", "remove-ns", "require", "use", "import", "refer", "refer-clojure", "alias", // Vars "def", "defonce", "declare", "intern", "var", // Metadata "meta", "with-meta", "vary-meta", "alter-meta!", "reset-meta!", // Threading macros "->", "->>", "as->", "cond->", "cond->>", "some->", "some->>", // Logic "and", "or", "not", "not=", // Other "assert", "comment", "doc", "lazy-seq", "delay", "force", "promise", "deliver", "future", "future-call", "pmap", "pcalls", "pvalues" ], "literal": ["true", "false", "nil"], "built_in": [ // Core functions - Collections "list", "list*", "vector", "vec", "hash-map", "hash-set", "sorted-map", "sorted-set", "sorted-map-by", "sorted-set-by", "seq", "cons", "conj", "concat", "lazy-cat", "mapcat", "cycle", "interleave", "interpose", "rest", "next", "butlast", "drop", "drop-while", "take", "take-nth", "take-while", "repeat", "replicate", "iterate", "range", "merge", "merge-with", "zipmap", "into", "reduce", "reduce-kv", "reductions", "set", "set/union", "set/intersection", "set/difference", "set/select", // Sequences "first", "second", "last", "rest", "next", "ffirst", "fnext", "nfirst", "nnext", "nth", "nthnext", "rand-nth", "when-first", "max-key", "min-key", "distinct", "filter", "remove", "keep", "keep-indexed", "for", "replace", "shuffle", "random-sample", "split-at", "split-with", "partition", "partition-all", "partition-by", "map", "map-indexed", "mapcat", "mapv", "pmap", "group-by", "frequencies", "reduce", "reductions", "transduce", // Collections - predicates "empty?", "not-empty", "seq?", "vector?", "list?", "map?", "set?", "coll?", "sequential?", "associative?", "sorted?", "counted?", "reversible?", // Collections - operations "count", "empty", "contains?", "get", "get-in", "assoc", "assoc-in", "dissoc", "update", "update-in", "select-keys", "rename-keys", "keys", "vals", "key", "val", "find", "peek", "pop", "conj", "disj", // Sequences - lazy "lazy-seq", "realized?", "doall", "dorun", // Strings "str", "subs", "format", "join", "escape", "split", "split-lines", "trim", "triml", "trimr", "trim-newline", "upper-case", "lower-case", "capitalize", "reverse", "replace", "replace-first", "re-find", "re-seq", "re-matches", "re-pattern", "re-matcher", "re-groups", // Numbers "inc", "dec", "max", "min", "abs", "+", "-", "*", "/", "quot", "rem", "mod", "bit-and", "bit-or", "bit-xor", "bit-not", "bit-shift-left", "bit-shift-right", "bit-flip", "bit-set", "bit-test", "bit-clear", "bit-and-not", "even?", "odd?", "zero?", "pos?", "neg?", "number?", "rational?", "integer?", "ratio?", "decimal?", "float?", "double?", // Math "rand", "rand-int", "rand-nth", "+", "-", "*", "/", "quot", "rem", "mod", "inc", "dec", "max", "min", "==", "<", ">", "<=", ">=", // Type predicates "nil?", "some?", "true?", "false?", "boolean?", "string?", "number?", "integer?", "int?", "pos-int?", "neg-int?", "nat-int?", "float?", "double?", "keyword?", "symbol?", "ident?", "simple-ident?", "qualified-ident?", "simple-symbol?", "qualified-symbol?", "simple-keyword?", "qualified-keyword?", "fn?", "ifn?", "coll?", "list?", "vector?", "map?", "set?", "seq?", "char?", "class?", "instance?", "var?", "identical?", "compare", // Functions "apply", "partial", "comp", "complement", "constantly", "identity", "fnil", "every-pred", "some-fn", "juxt", "memoize", "trampoline", // Atoms, Refs, Agents "atom", "swap!", "reset!", "compare-and-set!", "swap-vals!", "reset-vals!", "ref", "dosync", "ref-set", "alter", "commute", "ensure", "agent", "send", "send-off", "await", "await-for", "release-pending-sends", "restart-agent", "set-error-handler!", "set-error-mode!", "shutdown-agents", "add-watch", "remove-watch", // Vars "var-get", "var-set", "alter-var-root", "bound?", "thread-bound?", "with-bindings", "with-bindings*", "with-local-vars", "with-redefs", "push-thread-bindings", "pop-thread-bindings", "get-thread-bindings", // I/O "pr", "prn", "print", "println", "newline", "pr-str", "prn-str", "print-str", "println-str", "with-out-str", "with-in-str", "read", "read-line", "read-string", "slurp", "spit", "line-seq", // Namespaces "ns-name", "ns-map", "ns-interns", "ns-publics", "ns-imports", "ns-refers", "ns-aliases", "ns-resolve", "ns-unmap", "ns-unalias", "the-ns", "find-ns", "all-ns", "remove-ns", "symbol", "keyword", "namespace", "name", "gensym", // Evaluation "eval", "load", "load-file", "load-string", "load-reader", "requiring-resolve", "resolve", "macroexpand", "macroexpand-1", // Metadata "meta", "with-meta", "vary-meta", "alter-meta!", "reset-meta!", // Java interop "class", "type", "bases", "supers", "bean", "iterator-seq", "enumeration-seq", "format", "printf", // Transients "transient", "persistent!", "conj!", "assoc!", "dissoc!", "pop!", "disj!", // Sequences - sorting "sort", "sort-by", "sorted?", "compare", // Multimethods "defmulti", "defmethod", "remove-method", "remove-all-methods", "prefer-method", "methods", "get-method", "prefers", // Protocols "defprotocol", "extend", "extend-type", "extend-protocol", "reify", "satisfies?", "extenders", // Records and Types "defrecord", "deftype", "record?", "map->", "->", // Reducers "reduce", "fold", "filter", "remove", "map", "mapcat", "flatten", "take", "take-while", "drop", "drop-while", // Spec (clojure.spec.alpha) "def", "fdef", "keys", "valid?", "conform", "explain", "explain-str", "explain-data", "form", "describe", "assert", "check-asserts", // Testing "test", "deftest", "testing", "is", "are", "run-tests", "run-all-tests", // Core.async "go", "go-loop", "thread", "chan", "buffer", "dropping-buffer", "sliding-buffer", "timeout", "!!", "alts!!", "close!", "!", "alts!", "alt!", "alt!!", "put!", "take!", "offer!", "poll!", "onto-chan!", "to-chan!", "pipe", "pipeline", "pipeline-async", "pipeline-blocking", "split", "mix", "admix", "unmix", "mult", "tap", "untap", "pub", "sub", "unsub", "unsub-all" ] ] let contains: [Mode] = [ // Comments Mode(scope: "comment", begin: ";", end: "\n"), // Shebang Mode(scope: "comment", begin: "^#!", end: "\n"), // Discard form (reader macro) Mode(scope: "comment", begin: "#_"), // Keywords (with namespace support) Mode(scope: "meta", begin: "::?[a-zA-Z][a-zA-Z0-9*+!_?-]*(?:/[a-zA-Z][a-zA-Z0-9*+!_?-]*)?"), // Symbols (with namespace support) Mode(scope: "meta", begin: "[a-zA-Z*+!_?-][a-zA-Z0-9*+!_?-]*(?:/[a-zA-Z*+!_?-][a-zA-Z0-9*+!_?-]*)?"), // Regex literals Mode(scope: "string", begin: "#\"", end: "\""), // Strings CommonModes.stringDouble, // Characters Mode(scope: "string", begin: "\\\\(?:newline|space|tab|formfeed|backspace|return|u[0-9a-fA-F]{4}|o[0-7]{1,3}|.)"), // Anonymous function literal Mode(scope: "function", begin: "#\\("), // Set literal Mode(scope: "meta", begin: "#\\{"), // Var quote Mode(scope: "meta", begin: "#'"), // Tagged literals Mode(scope: "meta", begin: "#[a-zA-Z][a-zA-Z0-9*+!_?-]*(?:/[a-zA-Z][a-zA-Z0-9*+!_?-]*)?"), // Numbers // Ratio Mode(scope: "number", begin: "\\b[+-]?\\d+/\\d+\\b"), // Hex Mode(scope: "number", begin: "\\b[+-]?0[xX][0-9a-fA-F]+N?\\b"), // Octal Mode(scope: "number", begin: "\\b[+-]?0[0-7]+N?\\b"), // Scientific notation Mode(scope: "number", begin: "\\b[+-]?\\d+\\.?\\d*[eE][+-]?\\d+M?\\b"), // Float with M suffix (BigDecimal) Mode(scope: "number", begin: "\\b[+-]?\\d+\\.\\d+M\\b"), // Float Mode(scope: "number", begin: "\\b[+-]?\\d+\\.\\d+\\b"), // Integer with N suffix (BigInt) Mode(scope: "number", begin: "\\b[+-]?\\d+N\\b"), // Integer Mode(scope: "number", begin: "\\b[+-]?\\d+\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Cpp.swift ================================================ // // CPlusPlusLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct CppLanguage: LanguageDefinition { let name = "C++" let aliases: [String]? = ["cpp", "cc", "cxx", "c++", "hpp", "hh", "hxx", "h++"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ // C keywords "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", // C++ keywords "alignas", "alignof", "and", "and_eq", "asm", "bitand", "bitor", "bool", "catch", "class", "compl", "concept", "const_cast", "consteval", "constexpr", "constinit", "co_await", "co_return", "co_yield", "decltype", "delete", "dynamic_cast", "explicit", "export", "false", "friend", "mutable", "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected", "public", "reinterpret_cast", "requires", "static_assert", "static_cast", "template", "this", "thread_local", "throw", "true", "try", "typeid", "typename", "using", "virtual", "wchar_t", "xor", "xor_eq" ], "literal": ["true", "false", "nullptr", "NULL"], "built_in": [ // STL containers "std", "string", "wstring", "vector", "list", "deque", "set", "multiset", "map", "multimap", "unordered_set", "unordered_multiset", "unordered_map", "unordered_multimap", "stack", "queue", "priority_queue", "array", "bitset", "valarray", // Smart pointers "unique_ptr", "shared_ptr", "weak_ptr", "auto_ptr", // Streams "iostream", "istream", "ostream", "fstream", "ifstream", "ofstream", "stringstream", "istringstream", "ostringstream", "cin", "cout", "cerr", "clog", "wcin", "wcout", "wcerr", "wclog", // Algorithms "sort", "find", "find_if", "count", "count_if", "transform", "copy", "remove", "remove_if", "replace", "replace_if", "fill", "reverse", "rotate", "unique", "lower_bound", "upper_bound", "binary_search", "max", "min", "swap", "accumulate", "for_each", // Iterators "iterator", "const_iterator", "reverse_iterator", "const_reverse_iterator", "begin", "end", "rbegin", "rend", "cbegin", "cend", "crbegin", "crend", // Utilities "pair", "make_pair", "tuple", "make_tuple", "optional", "variant", "any", "move", "forward", "declval", // Memory "allocator", "make_unique", "make_shared", // Numeric types "int8_t", "int16_t", "int32_t", "int64_t", "uint8_t", "uint16_t", "uint32_t", "uint64_t", "size_t", "ptrdiff_t", "nullptr_t", // C standard library "printf", "scanf", "malloc", "calloc", "realloc", "free", "strlen", "strcpy", "strcmp", "memcpy", "memset", // Exception types "exception", "runtime_error", "logic_error", "out_of_range", "invalid_argument", "bad_alloc", "bad_cast", "bad_typeid" ] ] let contains: [Mode] = [ Mode(scope: "meta", begin: "^\\s*#\\s*(?:include|define|undef|if|ifdef|ifndef|else|elif|endif|error|pragma|line|warning)\\b.*$"), Mode(scope: "comment", begin: "//", end: "\n"), Mode(scope: "comment", begin: "/\\*", end: "\\*/"), Mode(scope: "meta", begin: "template\\s*<", end: ">"), Mode(scope: "class", begin: "\\b(?:class|struct)\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "\\bnamespace\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "function", begin: "\\b[a-zA-Z_][a-zA-Z0-9_]*\\s*(?=\\()"), // Raw string literals (C++11) Mode(scope: "string", begin: "R\"\\(", end: "\\)\""), CommonModes.stringDouble, Mode(scope: "string", begin: "'(?:[^'\\\\]|\\\\.)+'"), Mode(scope: "string", begin: "L'(?:[^'\\\\]|\\\\.)+'"), // Wide char Mode(scope: "string", begin: "u'(?:[^'\\\\]|\\\\.)+'"), // UTF-16 Mode(scope: "string", begin: "U'(?:[^'\\\\]|\\\\.)+'"), // UTF-32 // String literals with prefixes Mode(scope: "string", begin: "L\"(?:[^\"\\\\]|\\\\.)*\""), // Wide string Mode(scope: "string", begin: "u8\"(?:[^\"\\\\]|\\\\.)*\""), // UTF-8 Mode(scope: "string", begin: "u\"(?:[^\"\\\\]|\\\\.)*\""), // UTF-16 Mode(scope: "string", begin: "U\"(?:[^\"\\\\]|\\\\.)*\""), // UTF-32 // Числа // Binary (C++14) Mode(scope: "number", begin: "\\b0[bB][01]+[uUlL]*\\b"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+[uUlL]*\\b"), // Octal Mode(scope: "number", begin: "\\b0[0-7]+[uUlL]*\\b"), // Float/Double with suffixes Mode(scope: "number", begin: "\\b\\d+\\.\\d+[fFlL]?\\b"), Mode(scope: "number", begin: "\\b\\d+[eE][+-]?\\d+[fFlL]?\\b"), Mode(scope: "number", begin: "\\b\\d+\\.\\d+[eE][+-]?\\d+[fFlL]?\\b"), // Integer with digit separators (C++14) Mode(scope: "number", begin: "\\b\\d+(?:'\\d+)*[uUlL]*\\b"), // Integer Mode(scope: "number", begin: "\\b\\d+[uUlL]*\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Csharp.swift ================================================ // // CSharpLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct CSharpLanguage: LanguageDefinition { let name = "C#" let aliases: [String]? = ["csharp", "cs"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "abstract", "as", "base", "break", "case", "catch", "checked", "class", "const", "continue", "default", "delegate", "do", "else", "enum", "event", "explicit", "extern", "finally", "fixed", "for", "foreach", "goto", "if", "implicit", "in", "interface", "internal", "is", "lock", "namespace", "new", "operator", "out", "override", "params", "private", "protected", "public", "readonly", "ref", "return", "sealed", "sizeof", "stackalloc", "static", "struct", "switch", "this", "throw", "try", "typeof", "unchecked", "unsafe", "using", "virtual", "void", "volatile", "while", // Contextual keywords "add", "alias", "ascending", "async", "await", "by", "descending", "dynamic", "equals", "from", "get", "global", "group", "init", "into", "join", "let", "nameof", "notnull", "on", "orderby", "partial", "record", "remove", "select", "set", "unmanaged", "value", "var", "when", "where", "with", "yield", // C# 9.0+ "and", "not", "or", "nint", "nuint", // C# 10.0+ "file", "required", // C# 11.0+ "scoped" ], "literal": ["true", "false", "null"], "built_in": [ // Primitive types "bool", "byte", "sbyte", "char", "decimal", "double", "float", "int", "uint", "long", "ulong", "short", "ushort", "object", "string", // Common types "String", "Object", "Boolean", "Byte", "SByte", "Char", "Decimal", "Double", "Single", "Int16", "Int32", "Int64", "UInt16", "UInt32", "UInt64", "DateTime", "DateTimeOffset", "TimeSpan", "Guid", "Uri", "Version", // Collections "Array", "List", "Dictionary", "HashSet", "Queue", "Stack", "LinkedList", "SortedList", "SortedDictionary", "SortedSet", "Collection", "ObservableCollection", "IEnumerable", "ICollection", "IList", "IDictionary", "ISet", "IReadOnlyCollection", "IReadOnlyList", "IReadOnlyDictionary", // System types "Exception", "SystemException", "ArgumentException", "ArgumentNullException", "InvalidOperationException", "NotImplementedException", "NotSupportedException", "NullReferenceException", "IndexOutOfRangeException", "OverflowException", "DivideByZeroException", "FormatException", "IOException", "OutOfMemoryException", // Nullable "Nullable", // Delegates and events "Action", "Func", "Predicate", "EventHandler", "EventArgs", // Tasks and async "Task", "ValueTask", "CancellationToken", "CancellationTokenSource", // LINQ "Enumerable", "Queryable", "IQueryable", // Attributes "Attribute", "Obsolete", "Serializable", "DllImport", "StructLayout", "MethodImpl", "CallerMemberName", "CallerFilePath", "CallerLineNumber", // StringBuilder "StringBuilder", // Regex "Regex", "Match", "MatchCollection", "Group", "Capture", // IO "File", "Directory", "Path", "FileStream", "StreamReader", "StreamWriter", "MemoryStream", "BinaryReader", "BinaryWriter", "FileInfo", "DirectoryInfo", // Reflection "Type", "Assembly", "MethodInfo", "PropertyInfo", "FieldInfo", "ConstructorInfo", "MemberInfo", "ParameterInfo", // Generics "IComparable", "IEquatable", "IDisposable", "IAsyncDisposable", "ICloneable", "IConvertible", "IFormattable", "IFormatProvider", // Threading "Thread", "ThreadPool", "Monitor", "Mutex", "Semaphore", "AutoResetEvent", "ManualResetEvent", "ReaderWriterLock", "ReaderWriterLockSlim", // Console "Console", "Environment", // Convert "Convert", "BitConverter", "Encoding", // Math "Math", "Random", // Tuple "Tuple", "ValueTuple" ] ] let contains: [Mode] = [ Mode(scope: "comment.doc", begin: "///", end: "\n"), Mode(scope: "comment", begin: "/\\*", end: "\\*/"), Mode(scope: "comment", begin: "//", end: "\n"), Mode(scope: "meta", begin: "^\\s*#\\s*(?:if|else|elif|endif|define|undef|warning|error|line|region|endregion|pragma)\\b.*$"), Mode(scope: "meta", begin: "\\[", end: "\\]"), Mode(scope: "class", begin: "\\b(?:class|interface|struct|enum|record)\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "\\bnamespace\\s+([a-zA-Z_][a-zA-Z0-9_.]*)"), Mode(scope: "function", begin: "\\b[a-zA-Z_][a-zA-Z0-9_]*\\s*(?=\\()"), // Verbatim string literals Mode(scope: "string", begin: "@\"", end: "\""), // Interpolated strings Mode(scope: "string", begin: "\\$\"", end: "\""), // Verbatim interpolated strings Mode(scope: "string", begin: "\\$@\"", end: "\""), Mode(scope: "string", begin: "@\\$\"", end: "\""), // Raw string literals (C# 11) Mode(scope: "string", begin: "\"\"\"", end: "\"\"\""), CommonModes.stringDouble, Mode(scope: "string", begin: "'(?:[^'\\\\]|\\\\.)+'"), // Binary (C# 7.0+) Mode(scope: "number", begin: "\\b0[bB][01_]+(?:[uUlLfFdDmM]+)?\\b"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F_]+(?:[uUlL]+)?\\b"), // Float/Double/Decimal with suffixes Mode(scope: "number", begin: "\\b\\d[0-9_]*\\.[0-9_]+(?:[eE][+-]?[0-9_]+)?[fFdDmM]?\\b"), Mode(scope: "number", begin: "\\b\\d[0-9_]*[eE][+-]?[0-9_]+[fFdDmM]?\\b"), // Integer with underscores (C# 7.0+) Mode(scope: "number", begin: "\\b\\d[0-9_]*(?:[uUlLfFdDmM]+)?\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Css.swift ================================================ // // CSSLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct CSSLanguage: LanguageDefinition { let name = "CSS" let aliases: [String]? = ["css"] let caseInsensitive = true let keywords: [String: [String]]? = [ "keyword": [ // At-rules "@charset", "@import", "@namespace", "@media", "@supports", "@page", "@font-face", "@keyframes", "@counter-style", "@font-feature-values", "@property", "@layer", "@container", "@scope", // Media query keywords "and", "not", "only", "or", // Important "!important", // Logical operators "from", "to" ], "literal": [ // Color keywords "transparent", "currentColor", "inherit", "initial", "unset", "revert", // Named colors (common ones) "black", "white", "red", "green", "blue", "yellow", "orange", "purple", "pink", "brown", "gray", "grey", "cyan", "magenta", "lime", "navy", "teal", "aqua", "maroon", "olive", "silver", "fuchsia", // System colors "ActiveBorder", "ActiveCaption", "AppWorkspace", "Background", "ButtonFace", "ButtonHighlight", "ButtonShadow", "ButtonText", "CaptionText", "GrayText", "Highlight", "HighlightText", "InactiveBorder", "InactiveCaption", "InactiveCaptionText", "InfoBackground", "InfoText", "Menu", "MenuText", "Scrollbar", "ThreeDDarkShadow", "ThreeDFace", "ThreeDHighlight", "ThreeDLightShadow", "ThreeDShadow", "Window", "WindowFrame", "WindowText" ], "built_in": [ // Properties - Layout "display", "position", "top", "right", "bottom", "left", "float", "clear", "z-index", "overflow", "overflow-x", "overflow-y", "overflow-wrap", "clip", "clip-path", "visibility", "isolation", // Properties - Box Model "width", "height", "min-width", "max-width", "min-height", "max-height", "margin", "margin-top", "margin-right", "margin-bottom", "margin-left", "padding", "padding-top", "padding-right", "padding-bottom", "padding-left", "border", "border-width", "border-style", "border-color", "border-top", "border-right", "border-bottom", "border-left", "border-top-width", "border-top-style", "border-top-color", "border-right-width", "border-right-style", "border-right-color", "border-bottom-width", "border-bottom-style", "border-bottom-color", "border-left-width", "border-left-style", "border-left-color", "border-radius", "border-top-left-radius", "border-top-right-radius", "border-bottom-right-radius", "border-bottom-left-radius", "border-image", "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", "border-image-outset", "box-sizing", "box-shadow", "outline", "outline-width", "outline-style", "outline-color", "outline-offset", // Properties - Background "background", "background-color", "background-image", "background-repeat", "background-position", "background-size", "background-attachment", "background-origin", "background-clip", "background-blend-mode", // Properties - Typography "color", "font", "font-family", "font-size", "font-weight", "font-style", "font-variant", "font-stretch", "font-size-adjust", "font-synthesis", "font-kerning", "font-variant-ligatures", "font-variant-position", "font-variant-caps", "font-variant-numeric", "font-variant-alternates", "font-variant-east-asian", "font-feature-settings", "font-variation-settings", "line-height", "letter-spacing", "word-spacing", "text-align", "text-align-last", "text-decoration", "text-decoration-line", "text-decoration-color", "text-decoration-style", "text-decoration-thickness", "text-underline-position", "text-underline-offset", "text-indent", "text-transform", "text-shadow", "text-overflow", "text-wrap", "white-space", "word-break", "word-wrap", "hyphens", "tab-size", "direction", "unicode-bidi", "writing-mode", "text-orientation", "vertical-align", // Properties - Flexbox "flex", "flex-direction", "flex-wrap", "flex-flow", "flex-grow", "flex-shrink", "flex-basis", "justify-content", "align-items", "align-self", "align-content", "order", "gap", "row-gap", "column-gap", // Properties - Grid "grid", "grid-template", "grid-template-columns", "grid-template-rows", "grid-template-areas", "grid-auto-columns", "grid-auto-rows", "grid-auto-flow", "grid-column", "grid-row", "grid-area", "grid-column-start", "grid-column-end", "grid-row-start", "grid-row-end", "justify-items", "justify-self", "place-items", "place-self", "place-content", // Properties - Transform & Animation "transform", "transform-origin", "transform-style", "transform-box", "perspective", "perspective-origin", "backface-visibility", "transition", "transition-property", "transition-duration", "transition-timing-function", "transition-delay", "animation", "animation-name", "animation-duration", "animation-timing-function", "animation-delay", "animation-iteration-count", "animation-direction", "animation-fill-mode", "animation-play-state", "animation-timeline", "rotate", "scale", "translate", // Properties - Filters & Effects "filter", "backdrop-filter", "opacity", "mix-blend-mode", "mask", "mask-image", "mask-mode", "mask-repeat", "mask-position", "mask-clip", "mask-origin", "mask-size", "mask-composite", // Properties - Lists & Counters "list-style", "list-style-type", "list-style-position", "list-style-image", "counter-reset", "counter-increment", "counter-set", // Properties - Tables "table-layout", "border-collapse", "border-spacing", "caption-side", "empty-cells", // Properties - Columns "columns", "column-width", "column-count", "column-gap", "column-rule", "column-rule-width", "column-rule-style", "column-rule-color", "column-span", "column-fill", "break-before", "break-after", "break-inside", // Properties - User Interface "cursor", "pointer-events", "resize", "user-select", "caret-color", "accent-color", "appearance", "outline", "scroll-behavior", "scroll-margin", "scroll-padding", "scroll-snap-type", "scroll-snap-align", "scroll-snap-stop", "overscroll-behavior", "touch-action", // Properties - Content "content", "quotes", "content-visibility", "contain", // Properties - Printing "page-break-before", "page-break-after", "page-break-inside", "orphans", "widows", // Properties - Other "all", "will-change", "object-fit", "object-position", "image-rendering", "image-orientation", "aspect-ratio", "inset", "inset-block", "inset-inline", // Property values - Display "block", "inline", "inline-block", "flex", "inline-flex", "grid", "inline-grid", "table", "table-row", "table-cell", "list-item", "none", "contents", "flow-root", // Property values - Position "static", "relative", "absolute", "fixed", "sticky", // Property values - Float "left", "right", "none", // Property values - Text align "center", "justify", "start", "end", // Property values - Border style "solid", "dashed", "dotted", "double", "groove", "ridge", "inset", "outset", "hidden", // Property values - Font weight "normal", "bold", "bolder", "lighter", // Property values - Font style "italic", "oblique", // Property values - Text decoration "underline", "overline", "line-through", // Property values - Text transform "uppercase", "lowercase", "capitalize", // Property values - White space "nowrap", "pre", "pre-wrap", "pre-line", // Property values - Overflow "visible", "hidden", "scroll", "auto", "clip", // Property values - Cursor "pointer", "default", "crosshair", "move", "text", "wait", "help", "grab", "grabbing", "zoom-in", "zoom-out", "not-allowed", "progress", // Property values - Repeat "repeat", "repeat-x", "repeat-y", "no-repeat", "space", "round", // Property values - Size "auto", "contain", "cover", // Property values - Flex/Grid "row", "column", "wrap", "nowrap", "flex-start", "flex-end", "space-between", "space-around", "space-evenly", "stretch", "baseline", // Units "px", "em", "rem", "vh", "vw", "vmin", "vmax", "%", "cm", "mm", "in", "pt", "pc", "ch", "ex", "fr", "deg", "rad", "grad", "turn", "s", "ms", // Functions "url", "rgb", "rgba", "hsl", "hsla", "calc", "var", "attr", "linear-gradient", "radial-gradient", "conic-gradient", "repeating-linear-gradient", "repeating-radial-gradient", "repeating-conic-gradient", "min", "max", "clamp", "minmax", "fit-content", "blur", "brightness", "contrast", "drop-shadow", "grayscale", "hue-rotate", "invert", "opacity", "saturate", "sepia", "rotate", "scale", "scaleX", "scaleY", "scaleZ", "scale3d", "skew", "skewX", "skewY", "translate", "translateX", "translateY", "translateZ", "translate3d", "matrix", "matrix3d", "perspective", "cubic-bezier", "steps", "counters", "symbols", "path", "polygon", "circle", "ellipse", "inset" ] ] let contains: [Mode] = [ // Multi-line comments Mode(scope: "comment", begin: "/\\*", end: "\\*/"), // Single-line comments (non-standard but used in preprocessors) Mode(scope: "comment", begin: "//", end: "\n"), // At-rules Mode(scope: "meta", begin: "@[a-z-]+"), // Selectors - IDs Mode(scope: "meta", begin: "#[a-zA-Z][a-zA-Z0-9_-]*"), // Selectors - Classes Mode(scope: "meta", begin: "\\.[a-zA-Z][a-zA-Z0-9_-]*"), // Selectors - Pseudo-classes Mode(scope: "meta", begin: ":[a-zA-Z][a-zA-Z0-9_-]*(?:\\([^)]*\\))?"), // Selectors - Pseudo-elements Mode(scope: "meta", begin: "::[a-zA-Z][a-zA-Z0-9_-]*"), // Selectors - Attribute selectors Mode(scope: "meta", begin: "\\[", end: "\\]"), // Property names Mode(scope: "keyword", begin: "\\b[a-z-]+(?=\\s*:)"), // Strings with double quotes CommonModes.stringDouble, // Strings with single quotes CommonModes.stringSingle, // URLs Mode(scope: "string", begin: "url\\(", end: "\\)"), // Important Mode(scope: "keyword", begin: "!important\\b"), // Functions Mode(scope: "function", begin: "\\b[a-z-]+\\("), // Variables (CSS custom properties) Mode(scope: "meta", begin: "--[a-zA-Z][a-zA-Z0-9_-]*"), Mode(scope: "function", begin: "var\\("), // Colors - Hex Mode(scope: "number", begin: "#[0-9a-fA-F]{3,8}\\b"), // Colors - RGB/RGBA Mode(scope: "function", begin: "rgba?\\("), // Colors - HSL/HSLA Mode(scope: "function", begin: "hsla?\\("), // Numbers with units Mode(scope: "number", begin: "\\b\\d+\\.?\\d*(?:px|em|rem|%|vh|vw|vmin|vmax|cm|mm|in|pt|pc|ch|ex|fr|deg|rad|grad|turn|s|ms)\\b"), // Plain numbers Mode(scope: "number", begin: "\\b\\d+\\.?\\d*\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Dart.swift ================================================ // // DartLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct DartLanguage: LanguageDefinition { let name = "Dart" let aliases: [String]? = ["dart"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "abstract", "as", "assert", "async", "await", "break", "case", "catch", "class", "const", "continue", "covariant", "default", "deferred", "do", "dynamic", "else", "enum", "export", "extends", "extension", "external", "factory", "false", "final", "finally", "for", "Function", "get", "hide", "if", "implements", "import", "in", "interface", "is", "late", "library", "mixin", "new", "null", "on", "operator", "part", "required", "rethrow", "return", "sealed", "set", "show", "static", "super", "switch", "sync", "this", "throw", "true", "try", "typedef", "var", "void", "while", "with", "yield", // Dart 3.0+ keywords "base", "final", "interface", "mixin", "sealed", "when" ], "literal": ["true", "false", "null"], "built_in": [ // Core types "int", "double", "num", "bool", "String", "Object", "Type", "Symbol", "List", "Set", "Map", "Runes", "StringBuffer", "RegExp", "Match", "Pattern", "DateTime", "Duration", "Uri", "Stopwatch", // Collections "Iterable", "Iterator", "LinkedHashMap", "LinkedHashSet", "HashMap", "HashSet", "SplayTreeMap", "SplayTreeSet", "Queue", "ListQueue", "DoubleLinkedQueue", "UnmodifiableListView", "UnmodifiableMapView", // Future and Stream "Future", "Stream", "StreamController", "StreamSubscription", "StreamTransformer", "Completer", "StreamSink", "EventSink", "FutureOr", "Zone", "ZoneSpecification", // Core functions "print", "identical", "identityHashCode", // Exceptions "Exception", "Error", "ArgumentError", "RangeError", "IndexError", "StateError", "UnsupportedError", "UnimplementedError", "CastError", "TypeError", "NoSuchMethodError", "NullThrownError", "FormatException", "IntegerDivisionByZeroException", "OutOfMemoryError", "StackOverflowError", "ConcurrentModificationError", "TimeoutException", // Math "Random", "Point", "Rectangle", "MutableRectangle", // Convert "Converter", "Codec", "Encoding", "utf8", "latin1", "ascii", "base64", "base64Url", "json", "JsonEncoder", "JsonDecoder", "JsonCodec", "LineSplitter", "StringConversionSink", // dart:io (common) "File", "Directory", "Link", "IOSink", "FileStat", "FileMode", "FileSystemEntity", "FileSystemEvent", "Platform", "stdin", "stdout", "stderr", "Process", "ProcessResult", "HttpClient", "HttpServer", "HttpRequest", "HttpResponse", "HttpHeaders", "Cookie", "WebSocket", "Socket", "ServerSocket", "RawSocket", "RawServerSocket", "InternetAddress", "NetworkInterface", // dart:async "Timer", "scheduleMicrotask", "runZoned", "runZonedGuarded", // dart:collection "LinkedList", "LinkedListEntry", "ListBase", "MapBase", "SetBase", "IterableBase", "UnmodifiableListBase", "UnmodifiableMapBase", // dart:typed_data "ByteData", "Endian", "Float32List", "Float64List", "Int8List", "Int16List", "Int32List", "Int64List", "Uint8List", "Uint16List", "Uint32List", "Uint64List", "Uint8ClampedList", "ByteBuffer", // Common methods "forEach", "map", "where", "reduce", "fold", "every", "any", "contains", "firstWhere", "lastWhere", "singleWhere", "take", "takeWhile", "skip", "skipWhile", "toList", "toSet", "join", "length", "isEmpty", "isNotEmpty", "first", "last", "single", "elementAt", "add", "addAll", "remove", "removeAt", "removeLast", "removeWhere", "retainWhere", "clear", "insert", "insertAll", "sort", "shuffle", "reversed", "indexOf", "lastIndexOf", "sublist", "getRange", "setRange", "fillRange", "replaceRange", "asMap", // String methods "substring", "trim", "trimLeft", "trimRight", "padLeft", "padRight", "startsWith", "endsWith", "split", "splitMapJoin", "replaceAll", "replaceFirst", "replaceRange", "toLowerCase", "toUpperCase", "compareTo", "codeUnitAt", "codeUnits", "runes", "characters", // Future methods "then", "catchError", "whenComplete", "timeout", "asStream", // Stream methods "listen", "asBroadcastStream", "where", "map", "asyncMap", "asyncExpand", "handleError", "expand", "take", "takeWhile", "skip", "skipWhile", "distinct", "first", "last", "single", "isEmpty", "length", "toList", "drain", "pipe", "transform", "reduce", "fold", "join", "contains", "any", "every", "firstWhere", "lastWhere", "singleWhere", "elementAt", // Flutter-related (common) "Widget", "StatelessWidget", "StatefulWidget", "State", "BuildContext", "Key", "GlobalKey", "ValueKey", "ObjectKey", "UniqueKey", "InheritedWidget", "BuildOwner", "Element", "RenderObject", // Annotations "override", "deprecated", "pragma", "required", "protected", "visibleForTesting", "immutable", "sealed", "nonVirtual", "mustCallSuper" ] ] let contains: [Mode] = [ // Documentation comments Mode(scope: "comment.doc", begin: "///", end: "\n"), Mode(scope: "comment.doc", begin: "/\\*\\*", end: "\\*/"), Mode(scope: "comment", begin: "/\\*", end: "\\*/"), Mode(scope: "comment", begin: "//", end: "\n"), // Metadata/Annotations Mode(scope: "meta", begin: "@[a-zA-Z_][a-zA-Z0-9_]*"), Mode(scope: "class", begin: "\\b(?:class|enum|mixin|extension)\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "\\babstract\\s+class\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "function", begin: "\\b[a-zA-Z_][a-zA-Z0-9_]*\\s*(?=\\()"), // Raw strings Mode(scope: "string", begin: "r\"\"\"", end: "\"\"\""), Mode(scope: "string", begin: "r'''", end: "'''"), Mode(scope: "string", begin: "r\"", end: "\""), Mode(scope: "string", begin: "r'", end: "'"), // Multi-line strings with interpolation Mode(scope: "string", begin: "\"\"\"", end: "\"\"\""), Mode(scope: "string", begin: "'''", end: "'''"), // Regular strings with interpolation Mode(scope: "string", begin: "\"", end: "\""), Mode(scope: "string", begin: "'", end: "'"), // Symbols Mode(scope: "meta", begin: "#[a-zA-Z_][a-zA-Z0-9_]*"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+\\b"), // Scientific notation Mode(scope: "number", begin: "\\b\\d+\\.?\\d*[eE][+-]?\\d+\\b"), // Float Mode(scope: "number", begin: "\\b\\d+\\.\\d+\\b"), // Integer Mode(scope: "number", begin: "\\b\\d+\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Erlang.swift ================================================ // // ErlangLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct ErlangLanguage: LanguageDefinition { let name = "Erlang" let aliases: [String]? = ["erlang", "erl"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ // Control flow "after", "and", "andalso", "band", "begin", "bnot", "bor", "bsl", "bsr", "bxor", "case", "catch", "cond", "div", "end", "fun", "if", "let", "not", "of", "or", "orelse", "receive", "rem", "try", "when", "xor", // Module attributes "module", "export", "import", "compile", "vsn", "author", "copyright", "doc", "behaviour", "behavior", "record", "include", "include_lib", "define", "undef", "ifdef", "ifndef", "else", "endif", "error", "warning", // Special forms "query", "spec", "type", "opaque", "callback", "export_type" ], "literal": ["true", "false"], "built_in": [ // BIFs (Built-In Functions) "abs", "adler32", "adler32_combine", "alive", "apply", "atom_to_binary", "atom_to_list", "binary_to_atom", "binary_to_existing_atom", "binary_to_list", "binary_to_term", "bit_size", "bitstring_to_list", "byte_size", "ceil", "check_process_code", "date", "delete_module", "demonitor", "disconnect_node", "display", "element", "erase", "error", "exit", "float", "float_to_list", "floor", "function_exported", "garbage_collect", "get", "get_keys", "group_leader", "halt", "hd", "integer_to_list", "iolist_size", "iolist_to_binary", "is_alive", "is_atom", "is_binary", "is_bitstring", "is_boolean", "is_builtin", "is_float", "is_function", "is_integer", "is_list", "is_map", "is_number", "is_pid", "is_port", "is_process_alive", "is_record", "is_reference", "is_tuple", "length", "link", "list_to_atom", "list_to_binary", "list_to_bitstring", "list_to_existing_atom", "list_to_float", "list_to_integer", "list_to_pid", "list_to_tuple", "load_module", "loaded", "localtime", "localtime_to_universaltime", "make_ref", "map_size", "max", "md5", "md5_final", "md5_init", "md5_update", "memory", "min", "module_loaded", "monitor", "monitor_node", "node", "nodes", "now", "open_port", "pid_to_list", "port_close", "port_command", "port_connect", "port_control", "port_info", "port_to_list", "process_display", "process_flag", "process_info", "purge_module", "put", "register", "registered", "round", "self", "send", "send_after", "send_nosuspend", "set_cookie", "setelement", "size", "spawn", "spawn_link", "spawn_monitor", "spawn_opt", "split_binary", "start_timer", "statistics", "system_flag", "system_info", "system_monitor", "system_profile", "term_to_binary", "throw", "time", "tl", "trace", "trace_delivered", "trace_info", "trace_pattern", "trunc", "tuple_size", "tuple_to_list", "unalias", "universaltime", "universaltime_to_localtime", "unlink", "unregister", "whereis", // Process dictionary "erase", "get", "get_keys", "put", // Ports "open_port", "port_call", "port_close", "port_command", "port_connect", "port_control", "port_info", // Lists module "all", "any", "append", "concat", "delete", "dropwhile", "duplicate", "filter", "filtermap", "flatlength", "flatmap", "flatten", "foldl", "foldr", "foreach", "keydelete", "keyfind", "keymap", "keymember", "keymerge", "keyreplace", "keysearch", "keysort", "keystore", "keytake", "last", "map", "mapfoldl", "mapfoldr", "max", "member", "merge", "min", "nth", "nthtail", "partition", "prefix", "reverse", "search", "seq", "sort", "split", "splitwith", "sublist", "subtract", "suffix", "sum", "takewhile", "ukeymerge", "ukeysort", "umerge", "uniq", "unzip", "unzip3", "usort", "zip", "zip3", "zipwith", "zipwith3", // String module "centre", "chars", "chr", "concat", "copies", "cspan", "equal", "join", "left", "len", "lexemes", "lowercase", "rchr", "replace", "right", "rstr", "slice", "span", "split", "str", "strip", "sub_string", "sub_word", "substr", "take", "tokens", "to_float", "to_integer", "to_lower", "to_upper", "trim", "uppercase", "words", // Binary module "at", "bin_to_list", "compile_pattern", "copy", "decode_unsigned", "encode_unsigned", "first", "last", "list_to_bin", "longest_common_prefix", "longest_common_suffix", "match", "matches", "part", "referenced_byte_size", "replace", "split", // Maps module "filter", "filtermap", "find", "fold", "foreach", "from_keys", "from_list", "get", "groups_from_list", "intersect", "intersect_with", "is_key", "iterator", "keys", "map", "merge", "merge_with", "new", "next", "put", "remove", "size", "take", "to_list", "update", "update_with", "values", "with", "without", // IO module "format", "fread", "fwrite", "get_chars", "get_line", "nl", "parse_erl_exprs", "parse_erl_form", "put_chars", "read", "scan_erl_exprs", "scan_erl_form", "write", // File module "close", "consult", "copy", "delete", "get_cwd", "list_dir", "make_dir", "open", "position", "pread", "pwrite", "read", "read_file", "read_file_info", "read_link", "read_link_info", "rename", "script", "set_cwd", "sync", "truncate", "write", "write_file", "write_file_info", // Process related "monitor", "demonitor", "link", "unlink", "spawn", "spawn_link", "spawn_monitor", "spawn_opt", "exit", "register", "unregister", "whereis", "send", "send_after", "send_nosuspend", // Ets (Erlang Term Storage) "all", "delete", "delete_all_objects", "delete_object", "file2tab", "first", "foldl", "foldr", "from_dets", "fun2ms", "give_away", "i", "info", "init_table", "insert", "insert_new", "is_compiled_ms", "last", "lookup", "lookup_element", "match", "match_delete", "match_object", "match_spec_compile", "match_spec_run", "member", "new", "next", "prev", "rename", "repair_continuation", "safe_fixtable", "select", "select_count", "select_delete", "select_replace", "select_reverse", "setopts", "slot", "tab2file", "tab2list", "tabfile_info", "table", "take", "test_ms", "to_dets", "update_counter", "update_element", "whereis", // Gen_server, gen_statem, supervisor behaviors "start", "start_link", "stop", "call", "cast", "reply", "abcast", "multi_call", "enter_loop", "init", "handle_call", "handle_cast", "handle_info", "terminate", "code_change", "format_status", // OTP application "ensure_all_started", "ensure_started", "get_all_env", "get_all_key", "get_application", "get_env", "get_key", "load", "loaded_applications", "set_env", "start", "start_type", "stop", "takeover", "unload", "unset_env", "which_applications", // Common records "state", "mod", "id" ] ] let contains: [Mode] = [ // Comments Mode(scope: "comment", begin: "%", end: "\n"), // Module attributes Mode(scope: "meta", begin: "^-\\s*(?:module|export|import|compile|vsn|author|copyright|behaviour|behavior|record|include|include_lib|define|undef|ifdef|ifndef|else|endif|error|warning|spec|type|opaque|callback|export_type)\\b"), // Function definitions Mode(scope: "function", begin: "^([a-z][a-zA-Z0-9_@]*)\\s*\\("), // Atoms Mode(scope: "meta", begin: "'", end: "'"), Mode(scope: "meta", begin: "\\b[a-z][a-zA-Z0-9_@]*\\b(?!\\s*\\()"), // Variables (start with uppercase or underscore) Mode(scope: "meta", begin: "\\b[A-Z_][a-zA-Z0-9_@]*\\b"), // Macros Mode(scope: "meta", begin: "\\?[a-zA-Z][a-zA-Z0-9_@]*"), // Records Mode(scope: "class", begin: "#[a-z][a-zA-Z0-9_@]*"), // Strings CommonModes.stringDouble, // Binaries Mode(scope: "string", begin: "<<", end: ">>"), // Character literals Mode(scope: "string", begin: "\\$(?:[^\\\\]|\\\\(?:[bdefnrstv\\\\'\"]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]+\\}|\\^[@-_]))"), // Numbers // Float Mode(scope: "number", begin: "\\b\\d+\\.\\d+(?:[eE][+-]?\\d+)?\\b"), // Based integers (e.g., 16#FF, 2#1010) Mode(scope: "number", begin: "\\b\\d+#[0-9a-zA-Z]+\\b"), // Integer Mode(scope: "number", begin: "\\b\\d+\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Go.swift ================================================ // // GoLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct GoLanguage: LanguageDefinition { let name = "Go" let aliases: [String]? = ["go", "golang"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "break", "case", "chan", "const", "continue", "default", "defer", "else", "fallthrough", "for", "func", "go", "goto", "if", "import", "interface", "map", "package", "range", "return", "select", "struct", "switch", "type", "var" ], "literal": ["true", "false", "nil", "iota"], "built_in": [ // Базовые типы "bool", "byte", "rune", "string", "error", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr", "float32", "float64", "complex64", "complex128", // Встроенные функции "append", "cap", "close", "complex", "copy", "delete", "imag", "len", "make", "new", "panic", "print", "println", "real", "recover", // Типы и интерфейсы "any", "comparable", // Пакет fmt "Print", "Printf", "Println", "Sprint", "Sprintf", "Sprintln", "Fprint", "Fprintf", "Fprintln", "Scan", "Scanf", "Scanln", "Fscan", "Fscanf", "Fscanln", "Sscan", "Sscanf", "Sscanln", "Errorf", // Пакет errors "New", "Is", "As", "Unwrap", // Пакет io "Reader", "Writer", "ReadWriter", "ReadCloser", "WriteCloser", "ReadWriteCloser", "Copy", "ReadAll", "ReadFull", "WriteString", "EOF", "Closer", // Пакет os "File", "Open", "Create", "OpenFile", "Stdin", "Stdout", "Stderr", "Args", "Getenv", "Setenv", "Exit", "Remove", "RemoveAll", "Mkdir", "MkdirAll", "Chdir", "Getwd", // Пакет time "Time", "Duration", "Now", "Since", "Until", "Sleep", "After", "Ticker", "Timer", "Parse", "ParseDuration", "Second", "Minute", "Hour", "Millisecond", "Microsecond", "Nanosecond", // Пакет strings "Contains", "ContainsAny", "Count", "HasPrefix", "HasSuffix", "Index", "Join", "Replace", "Split", "ToLower", "ToUpper", "Trim", "TrimSpace", "Builder", // Пакет strconv "Atoi", "Itoa", "ParseBool", "ParseFloat", "ParseInt", "ParseUint", "FormatBool", "FormatFloat", "FormatInt", "FormatUint", // Пакет bytes "Buffer", "Equal", "Compare", // Пакет sync "Mutex", "RWMutex", "WaitGroup", "Once", "Cond", "Pool", "Map", "Lock", "Unlock", "RLock", "RUnlock", "Wait", "Done", "Add", // Пакет context "Context", "Background", "TODO", "WithCancel", "WithDeadline", "WithTimeout", "WithValue", // Пакет http "Request", "Response", "Client", "Server", "Handler", "HandlerFunc", "Get", "Post", "Head", "ListenAndServe", "Handle", "HandleFunc", "StatusOK", "StatusNotFound", "StatusInternalServerError", // Пакет json "Marshal", "Unmarshal", "Encoder", "Decoder", "RawMessage", // Пакет regexp "Regexp", "Compile", "MustCompile", "Match", "MatchString", "FindString", "FindAllString", // Пакет sort "Sort", "Slice", "Strings", "Ints", "Float64s", "Search", // Пакет math "Abs", "Ceil", "Floor", "Max", "Min", "Pow", "Sqrt", "Round", "Sin", "Cos", "Tan", "Pi", "E", "Inf", "NaN", "IsNaN", "IsInf" ] ] let contains: [Mode] = [ Mode(scope: "comment", begin: "/\\*", end: "\\*/"), Mode(scope: "comment", begin: "//", end: "\n"), Mode(scope: "function", begin: "\\bfunc\\s+(?:\\([^)]*\\)\\s+)?([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "\\btype\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "\\b(?:struct|interface)\\b"), // Raw string literals (backticks) Mode(scope: "string", begin: "`", end: "`"), Mode(scope: "string", begin: "\"", end: "\""), Mode(scope: "string", begin: "'(?:[^'\\\\]|\\\\.)+'"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+(?:\\.[0-9a-fA-F]+)?[pP]?[+-]?\\d*\\b"), // Octal Mode(scope: "number", begin: "\\b0[oO][0-7]+\\b"), // Binary Mode(scope: "number", begin: "\\b0[bB][01]+\\b"), // Float with exponent Mode(scope: "number", begin: "\\b\\d+(?:\\.\\d+)?[eE][+-]?\\d+[i]?\\b"), // Float Mode(scope: "number", begin: "\\b\\d+\\.\\d+[i]?\\b"), // Integer with underscores Mode(scope: "number", begin: "\\b\\d+(?:_\\d+)*[i]?\\b"), // Imaginary numbers Mode(scope: "number", begin: "\\b\\d+i\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Groovy.swift ================================================ // // GroovyLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct GroovyLanguage: LanguageDefinition { let name = "Groovy" let aliases: [String]? = ["groovy", "gvy", "gy", "gsh"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "abstract", "as", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "def", "default", "do", "double", "else", "enum", "extends", "false", "final", "finally", "float", "for", "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "trait", "transient", "true", "try", "void", "volatile", "while", // Groovy-specific "as", "def", "in", "trait", "var" ], "literal": ["true", "false", "null"], "built_in": [ // Primitive wrapper classes "Boolean", "Byte", "Character", "Double", "Float", "Integer", "Long", "Number", "Short", "String", "Void", // Common classes "Object", "Class", "Closure", "Range", "Binding", "Script", "GroovyObject", "MetaClass", "ExpandoMetaClass", "Category", "Mixin", // Collections "Collection", "List", "ArrayList", "LinkedList", "Set", "HashSet", "LinkedHashSet", "TreeSet", "SortedSet", "Map", "HashMap", "LinkedHashMap", "TreeMap", "Hashtable", "Properties", "Queue", "Stack", "Vector", // GDK enhancements "each", "eachWithIndex", "collect", "collectMany", "findAll", "find", "findIndexOf", "findLastIndexOf", "grep", "every", "any", "inject", "sum", "max", "min", "sort", "unique", "reverse", "flatten", "transpose", "intersect", "disjoint", "plus", "minus", "multiply", "power", "div", "leftShift", "rightShift", "getAt", "putAt", "asType", "asBoolean", "split", "tokenize", "join", "reverse", "padLeft", "padRight", "center", "capitalize", "uncapitalize", "toUpperCase", "toLowerCase", "trim", "stripIndent", "stripMargin", "eachLine", "readLines", "splitEachLine", "withReader", "withWriter", "withStream", "withInputStream", "withOutputStream", "withPrintWriter", "eachFile", "eachDir", "eachFileRecurse", "eachDirRecurse", "eachFileMatch", "eachDirMatch", "traverse", "getText", "getBytes", "newReader", "newWriter", "newInputStream", "newOutputStream", "newPrintWriter", "append", "write", "leftShift", // I/O "File", "FileInputStream", "FileOutputStream", "FileReader", "FileWriter", "BufferedReader", "BufferedWriter", "InputStreamReader", "OutputStreamWriter", "PrintWriter", "PrintStream", "InputStream", "OutputStream", "Reader", "Writer", "RandomAccessFile", // Date/Time "Date", "Calendar", "GregorianCalendar", "TimeZone", "SimpleDateFormat", "DateFormat", "LocalDate", "LocalTime", "LocalDateTime", "ZonedDateTime", "Duration", "Period", "Instant", // Utilities "Random", "UUID", "Timer", "TimerTask", "Optional", // Regex "Pattern", "Matcher", "RegEx", // SQL/Database "Sql", "GroovyRowResult", "DataSet", // XML "XmlParser", "XmlSlurper", "MarkupBuilder", "StreamingMarkupBuilder", "Node", "NodeList", "GPathResult", // JSON "JsonSlurper", "JsonBuilder", "JsonOutput", "StreamingJsonBuilder", // HTTP/Network "URL", "URI", "URLConnection", "HttpURLConnection", "Socket", "ServerSocket", "InetAddress", // Swing (common in Groovy) "SwingBuilder", "JFrame", "JPanel", "JButton", "JLabel", "JTextField", "JTextArea", "JTable", "JList", "JTree", "JMenu", "JMenuItem", // Concurrency "Thread", "Runnable", "Callable", "Future", "ExecutorService", "ThreadPoolExecutor", "ScheduledExecutorService", "Lock", "ReentrantLock", "Semaphore", "CountDownLatch", "CyclicBarrier", // Groovy SQL "Sql", "DataSource", "Connection", "Statement", "PreparedStatement", "ResultSet", "ResultSetMetaData", // Exceptions "Exception", "RuntimeException", "Throwable", "Error", "IllegalArgumentException", "IllegalStateException", "NullPointerException", "IndexOutOfBoundsException", "ArrayIndexOutOfBoundsException", "ClassNotFoundException", "ClassCastException", "NumberFormatException", "IOException", "FileNotFoundException", "SQLException", // Annotations "Override", "Deprecated", "SuppressWarnings", "FunctionalInterface", "CompileStatic", "TypeChecked", "ToString", "EqualsAndHashCode", "TupleConstructor", "Canonical", "Immutable", "Singleton", "Delegate", "Lazy", "Newify", "Sortable", "Field", "PackageScope", "BaseScript", // AST Transformations "ASTTest", "AutoClone", "AutoExternalize", "Builder", "Canonical", "Category", "CompileDynamic", "CompileStatic", "Delegate", "EqualsAndHashCode", "ExternalizeMethods", "Field", "Grab", "GrabConfig", "GrabExclude", "GrabResolver", "Grapes", "Immutable", "IndexedProperty", "InheritConstructors", "Lazy", "ListenerList", "Log", "Memoized", "Mixin", "Newify", "NotYetImplemented", "PackageScope", "Singleton", "Sortable", "Synchronized", "ThreadInterrupt", "TimedInterrupt", "ToString", "TupleConstructor", "TypeChecked", "Vetoable", "VisibilityOptions", "WithReadLock", "WithWriteLock", // Testing (Spock, etc.) "Specification", "given", "when", "then", "expect", "where", "and", "cleanup", "setup", "setupSpec", "cleanupSpec", // Build tools (Gradle) "task", "tasks", "dependencies", "repositories", "buildscript", "allprojects", "subprojects", "apply", "plugin", "ext", "configurations", "sourceSets", "jar", "war", "test", "build", "clean", "assemble", "compile", "implementation", "api", "testImplementation", "testCompile" ] ] let contains: [Mode] = [ Mode(scope: "comment.doc", begin: "/\\*\\*", end: "\\*/"), Mode(scope: "comment", begin: "/\\*", end: "\\*/"), Mode(scope: "comment", begin: "//", end: "\n"), Mode(scope: "comment", begin: "^#!", end: "\n"), Mode(scope: "meta", begin: "@[a-zA-Z_][a-zA-Z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)*"), Mode(scope: "class", begin: "\\b(?:class|interface|trait|enum)\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "function", begin: "\\b(?:def|void|public|private|protected|static)\\s+[a-zA-Z_][a-zA-Z0-9_]*\\s*(?=\\()"), // Triple-quoted strings (multi-line) Mode(scope: "string", begin: "\"\"\"", end: "\"\"\""), Mode(scope: "string", begin: "'''", end: "'''"), // Slashy strings (regex-friendly) Mode(scope: "string", begin: "/(?![*/])", end: "/"), // Dollar slashy strings Mode(scope: "string", begin: "\\$/", end: "/\\$"), // GString (interpolated strings) Mode(scope: "string", begin: "\"", end: "\""), // Regular strings (single quotes, no interpolation) CommonModes.stringSingle, // Character literals Mode(scope: "string", begin: "'(?:[^'\\\\]|\\\\.)'"), // Closures highlighting Mode(scope: "function", begin: "\\{", end: "\\}"), // Binary Mode(scope: "number", begin: "\\b0[bB][01]+[lLgGiI]?\\b"), // Octal Mode(scope: "number", begin: "\\b0[0-7]+[lLgGiI]?\\b"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+[lLgGiI]?\\b"), // Float/Double with suffixes Mode(scope: "number", begin: "\\b\\d+\\.\\d+(?:[eE][+-]?\\d+)?[fFdDgG]?\\b"), Mode(scope: "number", begin: "\\b\\d+[eE][+-]?\\d+[fFdDgG]?\\b"), // BigDecimal (G suffix) Mode(scope: "number", begin: "\\b\\d+[gG]\\b"), // BigInteger (G or I suffix) Mode(scope: "number", begin: "\\b\\d+[iI]\\b"), // Long (L suffix) Mode(scope: "number", begin: "\\b\\d+[lL]\\b"), // Integer Mode(scope: "number", begin: "\\b\\d+\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Haskell.swift ================================================ // // HaskellLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct HaskellLanguage: LanguageDefinition { let name = "Haskell" let aliases: [String]? = ["haskell", "hs"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "as", "case", "of", "class", "data", "family", "instance", "default", "deriving", "do", "forall", "foreign", "hiding", "if", "then", "else", "import", "infix", "infixl", "infixr", "let", "in", "mdo", "module", "newtype", "proc", "qualified", "rec", "type", "where", // GHC extensions "pattern", "role", "via", "stock", "anyclass", "newtype", // Special keywords "safe", "unsafe", "interruptible", "ccall", "stdcall", "cplusplus", "jvm", "dotnet" ], "literal": ["True", "False"], "built_in": [ // Basic types "Bool", "Char", "String", "Int", "Integer", "Float", "Double", "Rational", "Word", "IO", "Maybe", "Either", "Ordering", // Type constructors "Just", "Nothing", "Left", "Right", "LT", "EQ", "GT", // List types "[]", "[a]", // Tuple types "()", "(,)", "(,,)", "(,,,)", "(,,,,)", // Numeric types "Int8", "Int16", "Int32", "Int64", "Word8", "Word16", "Word32", "Word64", "Natural", "Scientific", // Function types "->", "=>", // Type classes "Eq", "Ord", "Show", "Read", "Enum", "Bounded", "Num", "Real", "Integral", "Fractional", "Floating", "RealFrac", "RealFloat", "Semigroup", "Monoid", "Functor", "Applicative", "Monad", "Alternative", "MonadPlus", "Foldable", "Traversable", "Category", "Arrow", "ArrowChoice", "ArrowApply", "ArrowLoop", // Common functions "map", "filter", "foldr", "foldl", "foldr1", "foldl1", "foldMap", "scanr", "scanl", "scanr1", "scanl1", "iterate", "repeat", "replicate", "cycle", "take", "drop", "takeWhile", "dropWhile", "splitAt", "span", "break", "reverse", "zip", "zip3", "zipWith", "zipWith3", "unzip", "unzip3", "concat", "concatMap", "and", "or", "any", "all", "sum", "product", "maximum", "minimum", "elem", "notElem", "lookup", "head", "last", "tail", "init", "null", "length", // List comprehensions helpers "enumFrom", "enumFromTo", "enumFromThen", "enumFromThenTo", // Functor/Applicative/Monad "fmap", "<$>", "<*>", "*>", "<*", "pure", "return", ">>=", ">>", "fail", "join", "liftM", "liftM2", "liftM3", "liftM4", "liftM5", "ap", "liftA", "liftA2", "liftA3", "<|>", "empty", "guard", "when", "unless", "forever", "void", "mapM", "mapM_", "forM", "forM_", "sequence", "sequence_", "replicateM", "replicateM_", "filterM", "zipWithM", "zipWithM_", "foldM", "foldM_", // Foldable/Traversable "fold", "foldMap", "foldr", "foldl", "foldr1", "foldl1", "toList", "null", "length", "elem", "maximum", "minimum", "sum", "product", "traverse", "sequenceA", "mapM", "sequence", // Maybe "maybe", "isJust", "isNothing", "fromJust", "fromMaybe", "listToMaybe", "maybeToList", "catMaybes", "mapMaybe", // Either "either", "lefts", "rights", "partitionEithers", "isLeft", "isRight", "fromLeft", "fromRight", // Bool "bool", "not", "otherwise", // Tuple "fst", "snd", "curry", "uncurry", "swap", // Char/String "lines", "words", "unlines", "unwords", "showChar", "showString", "readParen", "showParen", "lex", "reads", "shows", "read", "show", // Numeric functions "abs", "signum", "negate", "recip", "div", "mod", "quot", "rem", "divMod", "quotRem", "gcd", "lcm", "sqrt", "exp", "log", "logBase", "sin", "cos", "tan", "asin", "acos", "atan", "atan2", "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", "pi", "(**)", "(^)", "(^^)", "fromIntegral", "realToFrac", "truncate", "round", "ceiling", "floor", "toInteger", "toRational", "properFraction", "even", "odd", "succ", "pred", // Comparison "compare", "max", "min", "comparing", "on", // Function composition ".", "$", "$!", "flip", "const", "id", "until", "asTypeOf", "error", "errorWithoutStackTrace", "undefined", "seq", "deepseq", "force", // IO "putChar", "putStr", "putStrLn", "print", "getChar", "getLine", "getContents", "interact", "readFile", "writeFile", "appendFile", "readIO", "readLn", // Control structures "if", "then", "else", "case", "of", "let", "in", "where", "do", // Data structures "Map", "Set", "IntMap", "IntSet", "Seq", "Array", "Vector", "HashMap", "HashSet", "Text", "ByteString", // Common modules functions "insert", "delete", "member", "notMember", "lookup", "findWithDefault", "empty", "singleton", "fromList", "toList", "union", "intersection", "difference", "null", "size", "map", "filter", "partition", // Text/ByteString "pack", "unpack", "append", "concat", "intercalate", "split", "splitOn", "strip", "stripPrefix", "stripSuffix", "replace", "toLower", "toUpper", "reverse", "length", "null", "empty", // Parser combinators (common) "parse", "parseTest", "runParser", "many", "some", "optional", "between", "sepBy", "sepBy1", "endBy", "endBy1", "count", "chainl", "chainl1", "chainr", "chainr1", "choice", "option", "optionMaybe", "try", "lookAhead", "notFollowedBy", // Exceptions "Exception", "SomeException", "IOException", "ArithException", "ArrayException", "AssertionFailed", "AsyncException", "BlockedIndefinitelyOnMVar", "BlockedIndefinitelyOnSTM", "Deadlock", "ErrorCall", "NoMethodError", "PatternMatchFail", "RecConError", "RecSelError", "RecUpdError", "TypeError", "catch", "catchJust", "handle", "handleJust", "try", "tryJust", "evaluate", "throw", "throwIO", "throwTo", "assert", "finally", "bracket", "bracket_", "bracketOnError", "onException", // Concurrency "forkIO", "forkOS", "forkOn", "forkIOWithUnmask", "killThread", "threadDelay", "yield", "myThreadId", "throwTo", "MVar", "newMVar", "newEmptyMVar", "takeMVar", "putMVar", "readMVar", "swapMVar", "tryTakeMVar", "tryPutMVar", "isEmptyMVar", "withMVar", "modifyMVar", "modifyMVar_", "Chan", "newChan", "writeChan", "readChan", "dupChan", "STM", "atomically", "retry", "orElse", "TVar", "newTVar", "readTVar", "writeTVar", "modifyTVar", "modifyTVar'", "swapTVar", // Prelude re-exports "undefined", "error", "trace", "traceShow", "traceShowId" ] ] let contains: [Mode] = [ // Block comments (nested) Mode(scope: "comment", begin: "\\{-", end: "-\\}"), // Line comments Mode(scope: "comment", begin: "--", end: "\n"), // Pragmas Mode(scope: "meta", begin: "\\{-#", end: "#-\\}"), // Module declaration Mode(scope: "class", begin: "\\bmodule\\s+([A-Z][a-zA-Z0-9_]*(?:\\.[A-Z][a-zA-Z0-9_]*)*)"), // Type declarations Mode(scope: "class", begin: "\\b(?:data|newtype|type|class|instance)\\s+([A-Z][a-zA-Z0-9_]*)"), // Constructors (capital letter start) Mode(scope: "class", begin: "\\b[A-Z][a-zA-Z0-9_]*\\b"), // Function definitions Mode(scope: "function", begin: "^[a-z_][a-zA-Z0-9_']*\\s*(?:::|=)"), // Type variables (lowercase in type signatures) Mode(scope: "meta", begin: "\\b[a-z][a-zA-Z0-9_']*\\b(?=.*::)"), // Operators (custom) Mode(scope: "keyword", begin: "(?:[!#$%&*+./<=>?@\\\\^|~-]+|`[a-zA-Z_][a-zA-Z0-9_']*`)"), // Character literals Mode(scope: "string", begin: "'(?:[^'\\\\]|\\\\(?:[abfnrtv\\\\\"'&]|o[0-7]+|x[0-9a-fA-F]+|[0-9]+|\\^[@-_]|NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE|DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS|US|SP|DEL))'"), // String literals Mode(scope: "string", begin: "\"", end: "\""), // Multi-line strings (with backslash continuation) Mode(scope: "string", begin: "\"", end: "\""), // Numbers // Binary (GHC 7.10+) Mode(scope: "number", begin: "\\b0[bB][01]+\\b"), // Octal Mode(scope: "number", begin: "\\b0[oO][0-7]+\\b"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+\\b"), // Float with exponent Mode(scope: "number", begin: "\\b\\d+\\.\\d+(?:[eE][+-]?\\d+)?\\b"), Mode(scope: "number", begin: "\\b\\d+[eE][+-]?\\d+\\b"), // Float Mode(scope: "number", begin: "\\b\\d+\\.\\d+\\b"), // Integer Mode(scope: "number", begin: "\\b\\d+\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Html.swift ================================================ // // HTMLLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct HTMLLanguage: LanguageDefinition { let name = "HTML" let aliases: [String]? = ["html", "htm", "xhtml"] let caseInsensitive = true let keywords: [String: [String]]? = [ "keyword": [], "literal": [], "built_in": [ // Document structure "html", "head", "title", "base", "link", "meta", "style", "body", // Sections "header", "nav", "main", "section", "article", "aside", "footer", "h1", "h2", "h3", "h4", "h5", "h6", "hgroup", "address", // Grouping content "p", "hr", "pre", "blockquote", "ol", "ul", "li", "dl", "dt", "dd", "figure", "figcaption", "div", // Text-level semantics "a", "em", "strong", "small", "s", "cite", "q", "dfn", "abbr", "data", "time", "code", "var", "samp", "kbd", "sub", "sup", "i", "b", "u", "mark", "ruby", "rt", "rp", "bdi", "bdo", "span", "br", "wbr", // Edits "ins", "del", // Embedded content "img", "iframe", "embed", "object", "param", "video", "audio", "source", "track", "canvas", "map", "area", "svg", "math", // Tabular data "table", "caption", "colgroup", "col", "tbody", "thead", "tfoot", "tr", "td", "th", // Forms "form", "label", "input", "button", "select", "datalist", "optgroup", "option", "textarea", "output", "progress", "meter", "fieldset", "legend", // Interactive elements "details", "summary", "dialog", "menu", // Scripting "script", "noscript", "template", "slot", "canvas", // Web Components "template", "slot", // Obsolete/deprecated (but still used) "center", "font", "strike", "big", "tt", "frame", "frameset", "noframes", "acronym", "applet", "basefont", "dir", "isindex", "listing", "marquee", "plaintext", "xmp", "nextid", "rb", "rtc", // HTML5 new elements "article", "aside", "bdi", "command", "details", "summary", "figure", "figcaption", "footer", "header", "hgroup", "mark", "meter", "nav", "progress", "ruby", "rt", "rp", "section", "time", "wbr", "datalist", "keygen", "output", "canvas", "audio", "video", "source", "embed", "track", "main", "picture", // Attributes (common) "class", "id", "style", "title", "lang", "dir", "hidden", "tabindex", "accesskey", "contenteditable", "contextmenu", "draggable", "dropzone", "spellcheck", "translate", "role", "aria-label", "aria-labelledby", "aria-describedby", "aria-hidden", "data-", // Form attributes "action", "method", "enctype", "accept-charset", "novalidate", "autocomplete", "autofocus", "disabled", "readonly", "required", "placeholder", "pattern", "min", "max", "step", "minlength", "maxlength", "size", "multiple", "checked", "selected", "value", "name", "type", "for", "form", // Link/script attributes "href", "src", "alt", "crossorigin", "rel", "media", "hreflang", "type", "sizes", "async", "defer", "charset", "integrity", // Image/media attributes "width", "height", "loading", "decoding", "srcset", "sizes", "poster", "preload", "autoplay", "loop", "muted", "controls", "playsinline", // Table attributes "colspan", "rowspan", "headers", "scope", // Meta attributes "content", "http-equiv", "charset", "name", // Global event attributes "onclick", "ondblclick", "onmousedown", "onmouseup", "onmouseover", "onmousemove", "onmouseout", "onkeypress", "onkeydown", "onkeyup", "onfocus", "onblur", "onchange", "onsubmit", "onload", "onunload", "onerror", "onresize", "onscroll", "onwheel", "oncopy", "oncut", "onpaste", "ondrag", "ondragstart", "ondragend", "ondragover", "ondragenter", "ondragleave", "ondrop", "ontouchstart", "ontouchmove", "ontouchend", "ontouchcancel" ] ] let contains: [Mode] = [ // HTML comments Mode(scope: "comment", begin: ""), // DOCTYPE declaration Mode(scope: "meta", begin: ""), // XML declaration Mode(scope: "meta", begin: "<\\?xml", end: "\\?>"), // CDATA sections Mode(scope: "string", begin: ""), // Processing instructions Mode(scope: "meta", begin: "<\\?", end: "\\?>"), // Opening tags with attributes Mode(scope: "keyword", begin: "<[a-zA-Z][a-zA-Z0-9-]*"), // Closing tags Mode(scope: "keyword", begin: ""), // Self-closing tags Mode(scope: "keyword", begin: "/>"), // Attribute names Mode(scope: "meta", begin: "\\b[a-zA-Z][a-zA-Z0-9-]*(?==)"), // Attribute values with double quotes Mode(scope: "string", begin: "=\"", end: "\""), // Attribute values with single quotes Mode(scope: "string", begin: "='", end: "'"), // Entity references Mode(scope: "string", begin: "&[a-zA-Z]+;"), Mode(scope: "string", begin: "&#[0-9]+;"), Mode(scope: "string", begin: "&#x[0-9a-fA-F]+;"), // Tag closing bracket Mode(scope: "keyword", begin: ">"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Java.swift ================================================ // // JavaLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct JavaLanguage: LanguageDefinition { let name = "Java" let aliases: [String]? = ["java", "jsp"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package", "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile", "while", // Java 9+ "module", "requires", "exports", "opens", "to", "uses", "provides", "with", // Java 10+ "var", // Java 14+ "record", "sealed", "permits", "non-sealed", // Java 17+ "yield" ], "literal": ["true", "false", "null"], "built_in": [ // Primitive wrapper classes "Boolean", "Byte", "Character", "Double", "Float", "Integer", "Long", "Short", "Void", "Number", // Common classes "Object", "String", "StringBuffer", "StringBuilder", "Class", "System", "Thread", "Runnable", "Throwable", "Exception", "Error", "RuntimeException", // Collections "Collection", "List", "ArrayList", "LinkedList", "Vector", "Stack", "Set", "HashSet", "LinkedHashSet", "TreeSet", "SortedSet", "NavigableSet", "Map", "HashMap", "LinkedHashMap", "TreeMap", "Hashtable", "SortedMap", "NavigableMap", "Queue", "Deque", "PriorityQueue", "ArrayDeque", "Collections", "Arrays", "Iterator", "ListIterator", "Enumeration", // I/O "File", "FileInputStream", "FileOutputStream", "FileReader", "FileWriter", "BufferedReader", "BufferedWriter", "InputStreamReader", "OutputStreamWriter", "InputStream", "OutputStream", "Reader", "Writer", "PrintStream", "PrintWriter", "Scanner", "Console", // Utilities "Date", "Calendar", "GregorianCalendar", "TimeZone", "Locale", "Random", "UUID", "Optional", "Objects", "Math", "StrictMath", // Concurrency "Executor", "ExecutorService", "Callable", "Future", "CompletableFuture", "Lock", "ReentrantLock", "Semaphore", "CountDownLatch", "CyclicBarrier", "Atomic", "AtomicInteger", "AtomicLong", "AtomicBoolean", "AtomicReference", // Streams (Java 8+) "Stream", "IntStream", "LongStream", "DoubleStream", "Collector", "Collectors", // Functional interfaces (Java 8+) "Function", "BiFunction", "Consumer", "BiConsumer", "Supplier", "Predicate", "BiPredicate", "UnaryOperator", "BinaryOperator", // Exceptions "IOException", "FileNotFoundException", "SQLException", "ClassNotFoundException", "IllegalArgumentException", "IllegalStateException", "NullPointerException", "IndexOutOfBoundsException", "ArrayIndexOutOfBoundsException", "ConcurrentModificationException", "UnsupportedOperationException", "NumberFormatException", "ArithmeticException", "ClassCastException", // Annotations "Override", "Deprecated", "SuppressWarnings", "SafeVarargs", "FunctionalInterface", // Reflection "Method", "Field", "Constructor", "Modifier", // Generics helpers "Comparable", "Comparator", "Cloneable", "Serializable", "AutoCloseable" ] ] let contains: [Mode] = [ Mode(scope: "comment.doc", begin: "/\\*\\*", end: "\\*/"), Mode(scope: "comment", begin: "/\\*", end: "\\*/"), // Однострочные комментарии Mode(scope: "comment", begin: "//", end: "\n"), Mode(scope: "meta", begin: "@[a-zA-Z_][a-zA-Z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)*"), Mode(scope: "class", begin: "\\b(?:class|interface|enum|record)\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "function", begin: "\\b[a-zA-Z_][a-zA-Z0-9_]*\\s*(?=\\()"), // Text blocks (Java 15+) Mode(scope: "string", begin: "\"\"\"", end: "\"\"\""), CommonModes.stringDouble, Mode(scope: "string", begin: "'(?:[^'\\\\]|\\\\.)+'"), // Binary (Java 7+) Mode(scope: "number", begin: "\\b0[bB][01]+[lLfFdD]?\\b"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+[lLfFdD]?\\b"), // Octal Mode(scope: "number", begin: "\\b0[0-7]+[lL]?\\b"), // Float/Double with suffixes Mode(scope: "number", begin: "\\b\\d+\\.\\d+[fFdD]?\\b"), Mode(scope: "number", begin: "\\b\\d+[eE][+-]?\\d+[fFdD]?\\b"), Mode(scope: "number", begin: "\\b\\d+\\.\\d+[eE][+-]?\\d+[fFdD]?\\b"), // Integer with underscores (Java 7+) Mode(scope: "number", begin: "\\b\\d+(?:_\\d+)*[lLfFdD]?\\b"), // Integer Mode(scope: "number", begin: "\\b\\d+[lLfFdD]?\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/JavaScript.swift ================================================ // // JavaScriptLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 31.08.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct JavaScriptLanguage: LanguageDefinition { let name = "JavaScript" let aliases: [String]? = ["js", "jsx", "mjs", "cjs"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "constructor","this","as","in","of","if","for","while","finally","var","new","function", "do","return","void","else","break","catch","instanceof","with", "throw","case","default","try","switch","continue","typeof","delete", "let","yield","const","class","debugger","async","await","static", "import","from","export","extends","using" ], "literal": ["true","false","null","undefined","NaN","Infinity"], "built_in": [ "Object","Function","Boolean","Symbol","Math","Date","Number","BigInt", "String","RegExp","Array","Float32Array","Float64Array","Int8Array", "Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array", "Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet", "WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON", "Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy", "Intl","WebAssembly","Error","EvalError","InternalError","RangeError", "ReferenceError","SyntaxError","TypeError","URIError", "console","window","document","localStorage","sessionStorage","module","global","this","arguments","super" ] ] let contains: [Mode] = [ Mode(scope: "comment", begin: "//", end: "\n"), Mode(scope: "comment", begin: "#", end: "\n"), Mode(scope: "comment", begin: "/\\*", end: "\\*/"), Mode(scope: "comment.doc", begin: "/\\*\\*", end: "\\*/"), Mode(scope: "function", begin: "\\bfunction\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "\\bclass\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "\\bextends\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), CommonModes.stringDouble, CommonModes.stringSingle, Mode(scope: "string", begin: "`", end: "`"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Kotlin.swift ================================================ // // KotlinLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct KotlinLanguage: LanguageDefinition { let name = "Kotlin" let aliases: [String]? = ["kt", "kts"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "abstract", "actual", "annotation", "as", "break", "by", "catch", "class", "companion", "const", "constructor", "continue", "crossinline", "data", "do", "dynamic", "else", "enum", "expect", "external", "final", "finally", "for", "fun", "get", "if", "import", "in", "infix", "init", "inline", "inner", "interface", "internal", "is", "lateinit", "noinline", "object", "open", "operator", "out", "override", "package", "private", "protected", "public", "reified", "return", "sealed", "set", "super", "suspend", "tailrec", "this", "throw", "try", "typealias", "typeof", "val", "var", "vararg", "when", "where", "while", // Soft keywords "by", "catch", "constructor", "delegate", "dynamic", "field", "file", "finally", "get", "import", "init", "param", "property", "receiver", "set", "setparam", "where", // Modifiers "actual", "abstract", "annotation", "companion", "const", "crossinline", "data", "enum", "expect", "external", "final", "infix", "inline", "inner", "internal", "lateinit", "noinline", "open", "operator", "out", "override", "private", "protected", "public", "reified", "sealed", "suspend", "tailrec", "vararg" ], "literal": ["true", "false", "null"], "built_in": [ // Primitive types "Boolean", "Byte", "Char", "Double", "Float", "Int", "Long", "Short", "String", "Unit", "Nothing", "Any", // Unsigned types "UByte", "UShort", "UInt", "ULong", // Common types "Array", "BooleanArray", "ByteArray", "CharArray", "DoubleArray", "FloatArray", "IntArray", "LongArray", "ShortArray", "UByteArray", "UShortArray", "UIntArray", "ULongArray", "List", "MutableList", "Set", "MutableSet", "Map", "MutableMap", "ArrayList", "HashMap", "HashSet", "LinkedHashMap", "LinkedHashSet", "Collection", "MutableCollection", "Iterable", "MutableIterable", "Sequence", "Iterator", "MutableIterator", "ListIterator", "MutableListIterator", // Ranges "IntRange", "LongRange", "CharRange", "ClosedRange", "OpenEndRange", "IntProgression", "LongProgression", "CharProgression", // Result and optionals "Result", "Pair", "Triple", // Kotlin stdlib functions "print", "println", "readLine", "readln", "TODO", "require", "requireNotNull", "check", "checkNotNull", "error", "assert", "repeat", "run", "with", "let", "also", "apply", "takeIf", "takeUnless", "lazy", "runCatching", // Collections functions "listOf", "mutableListOf", "arrayListOf", "setOf", "mutableSetOf", "hashSetOf", "linkedSetOf", "sortedSetOf", "mapOf", "mutableMapOf", "hashMapOf", "linkedMapOf", "sortedMapOf", "emptyList", "emptySet", "emptyMap", "listOfNotNull", "arrayOf", "arrayOfNulls", "emptyArray", // String functions "buildString", "StringBuilder", // Coroutines "launch", "async", "runBlocking", "withContext", "coroutineScope", "supervisorScope", "delay", "yield", "Job", "Deferred", "CoroutineScope", "CoroutineContext", "Dispatchers", "Flow", "flow", "flowOf", "StateFlow", "SharedFlow", "Channel", // Delegates "Lazy", "ObservableProperty", "Delegates", // Reflection "KClass", "KFunction", "KProperty", "KType", "KCallable", // Annotations "Deprecated", "Suppress", "SuppressWarnings", "JvmStatic", "JvmField", "JvmOverloads", "JvmName", "Throws", "Synchronized", "Volatile", "Transient", // Exceptions "Exception", "RuntimeException", "IllegalArgumentException", "IllegalStateException", "IndexOutOfBoundsException", "NoSuchElementException", "NullPointerException", "ClassCastException", "NumberFormatException", "UnsupportedOperationException", "ArithmeticException", // Comparisons "Comparable", "Comparator", // Regex "Regex", "MatchResult", "MatchGroup", // Math "kotlin.math", // Standard functions "maxOf", "minOf", "coerceIn", "coerceAtLeast", "coerceAtMost" ] ] let contains: [Mode] = [ // KDoc comments Mode(scope: "comment.doc", begin: "/\\*\\*", end: "\\*/"), // Multistring Mode(scope: "comment", begin: "/\\*", end: "\\*/"), // Onestring Mode(scope: "comment", begin: "//", end: "\n"), // Annotattions Mode(scope: "meta", begin: "@[a-zA-Z_][a-zA-Z0-9_]*(?:::[a-zA-Z_][a-zA-Z0-9_]*)?"), Mode(scope: "meta", begin: "[a-zA-Z_][a-zA-Z0-9_]*@"), Mode(scope: "class", begin: "\\b(?:class|interface|object|enum class|data class|sealed class|sealed interface|value class|annotation class)\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "function", begin: "\\bfun\\s+(?:<[^>]*>\\s+)?([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "function", begin: "\\b(?:val|var)\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "string", begin: "\"\"\"", end: "\"\"\""), Mode(scope: "string", begin: "\"", end: "\""), Mode(scope: "string", begin: "'(?:[^'\\\\]|\\\\.)+'"), Mode(scope: "number", begin: "\\b0[bB][01_]+[uU]?[lL]?\\b"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F_]+[uU]?[lL]?\\b"), // Float/Double with suffixes Mode(scope: "number", begin: "\\b\\d[0-9_]*\\.[0-9_]+(?:[eE][+-]?[0-9_]+)?[fFdD]?\\b"), Mode(scope: "number", begin: "\\b\\d[0-9_]*[eE][+-]?[0-9_]+[fFdD]?\\b"), // Integer with underscores and suffixes Mode(scope: "number", begin: "\\b\\d[0-9_]*[uU]?[lL]?\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Lisp.swift ================================================ // // LispLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct LispLanguage: LanguageDefinition { let name = "Lisp" let aliases: [String]? = ["lisp", "cl", "common-lisp", "elisp", "emacs-lisp"] let caseInsensitive = true let keywords: [String: [String]]? = [ "keyword": [ // Special forms "quote", "function", "setq", "setf", "let", "let*", "flet", "labels", "macrolet", "symbol-macrolet", "if", "when", "unless", "cond", "case", "typecase", "and", "or", "prog1", "prog2", "progn", "progv", "go", "return", "return-from", "throw", "catch", "unwind-protect", "block", "tagbody", "multiple-value-call", "multiple-value-prog1", "the", "locally", "declare", "eval-when", "load-time-value", // Defining forms "defun", "defmacro", "defgeneric", "defmethod", "defclass", "defstruct", "deftype", "defvar", "defparameter", "defconstant", "define-compiler-macro", "define-modify-macro", "define-setf-expander", "define-symbol-macro", "defpackage", "in-package", "do-symbols", "do-external-symbols", "do-all-symbols", // Loop facility "loop", "do", "do*", "dotimes", "dolist", // Conditionals "if", "when", "unless", "cond", "case", "ecase", "typecase", "etypecase", "ctypecase", // Iteration "loop", "do", "do*", "dotimes", "dolist", "prog", "prog*", // Lambda "lambda", "function", "defun", "flet", "labels", // Macros "defmacro", "macrolet", "symbol-macrolet", "`", ",", ",@", // CLOS "defclass", "defgeneric", "defmethod", "call-next-method", "next-method-p", "slot-value", "with-slots", "with-accessors", "make-instance", "initialize-instance", "reinitialize-instance", "shared-initialize", "update-instance-for-different-class", "update-instance-for-redefined-class", "change-class", // Conditions "handler-case", "handler-bind", "ignore-errors", "define-condition", "make-condition", "signal", "error", "cerror", "warn", "invoke-restart", "invoke-restart-interactively", "restart-case", "restart-bind", "with-simple-restart", "abort", "continue", "muffle-warning", "store-value", "use-value", // Streams "with-open-file", "with-open-stream", "with-input-from-string", "with-output-to-string", // Other "values", "multiple-value-bind", "multiple-value-list", "multiple-value-setq", "nth-value" ], "literal": ["t", "nil"], "built_in": [ // Predicates "null", "atom", "symbolp", "numberp", "integerp", "floatp", "rationalp", "complexp", "characterp", "stringp", "consp", "listp", "arrayp", "vectorp", "bit-vector-p", "simple-vector-p", "simple-string-p", "simple-bit-vector-p", "functionp", "compiled-function-p", "packagep", "streamp", "readtablep", "hash-table-p", "pathnamep", "typep", "subtypep", "standard-char-p", "graphic-char-p", "alpha-char-p", "upper-case-p", "lower-case-p", "both-case-p", "digit-char-p", "alphanumericp", "evenp", "oddp", "zerop", "plusp", "minusp", "boundp", "fboundp", "special-operator-p", "constantp", "eq", "eql", "equal", "equalp", "endp", // Numeric functions "+", "-", "*", "/", "1+", "1-", "abs", "mod", "rem", "floor", "ceiling", "truncate", "round", "sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", "exp", "expt", "log", "sqrt", "isqrt", "conjugate", "phase", "realpart", "imagpart", "numerator", "denominator", "rational", "rationalize", "gcd", "lcm", "max", "min", "signum", "random", "random-state-p", "make-random-state", "incf", "decf", "=", "/=", "<", ">", "<=", ">=", // Bit operations "logand", "logior", "logxor", "logeqv", "lognand", "lognor", "logandc1", "logandc2", "logorc1", "logorc2", "lognot", "logtest", "logbitp", "logcount", "ash", "integer-length", "boole", "boole-and", "boole-ior", "boole-xor", "boole-eqv", "boole-nand", "boole-nor", "boole-andc1", "boole-andc2", "boole-orc1", "boole-orc2", "boole-c1", "boole-c2", "boole-set", "boole-clr", // List functions "car", "cdr", "caar", "cadr", "cdar", "cddr", "caaar", "caadr", "cadar", "caddr", "cdaar", "cdadr", "cddar", "cdddr", "cons", "list", "list*", "append", "nconc", "revappend", "nreconc", "butlast", "nbutlast", "last", "ldiff", "tailp", "nthcdr", "nth", "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth", "tenth", "rest", "member", "member-if", "member-if-not", "assoc", "assoc-if", "assoc-if-not", "rassoc", "rassoc-if", "rassoc-if-not", "acons", "pairlis", "copy-list", "copy-alist", "copy-tree", "subst", "subst-if", "subst-if-not", "nsubst", "nsubst-if", "nsubst-if-not", "sublis", "nsublis", "tree-equal", "list-length", "make-list", "push", "pop", "pushnew", "adjoin", "union", "nunion", "intersection", "nintersection", "set-difference", "nset-difference", "set-exclusive-or", "nset-exclusive-or", "subsetp", "mapc", "mapcar", "mapcan", "mapl", "maplist", "mapcon", // Sequence functions "length", "elt", "reverse", "nreverse", "sort", "stable-sort", "find", "find-if", "find-if-not", "position", "position-if", "position-if-not", "count", "count-if", "count-if-not", "mismatch", "search", "substitute", "substitute-if", "substitute-if-not", "nsubstitute", "nsubstitute-if", "nsubstitute-if-not", "concatenate", "merge", "remove", "remove-if", "remove-if-not", "delete", "delete-if", "delete-if-not", "remove-duplicates", "delete-duplicates", "subseq", "copy-seq", "fill", "replace", "reduce", "map", "map-into", "some", "every", "notany", "notevery", // String functions "char", "schar", "string", "string=", "string/=", "string<", "string>", "string<=", "string>=", "string-equal", "string-not-equal", "string-lessp", "string-greaterp", "string-not-greaterp", "string-not-lessp", "string-upcase", "string-downcase", "string-capitalize", "nstring-upcase", "nstring-downcase", "nstring-capitalize", "string-trim", "string-left-trim", "string-right-trim", "make-string", "char-code", "code-char", "character", "char-upcase", "char-downcase", "char=", "char/=", "char<", "char>", "char<=", "char>=", "char-equal", "char-not-equal", "char-lessp", "char-greaterp", "char-not-greaterp", "char-not-lessp", "char-name", "name-char", // Array functions "make-array", "array-dimensions", "array-dimension", "array-total-size", "array-rank", "array-element-type", "array-row-major-index", "aref", "svref", "bit", "sbit", "vector", "vector-push", "vector-push-extend", "vector-pop", "adjust-array", "adjustable-array-p", "array-in-bounds-p", "array-has-fill-pointer-p", "fill-pointer", "row-major-aref", // Hash table functions "make-hash-table", "gethash", "remhash", "maphash", "clrhash", "hash-table-count", "hash-table-rehash-size", "hash-table-rehash-threshold", "hash-table-size", "hash-table-test", "sxhash", "with-hash-table-iterator", // Symbol functions "symbol-name", "symbol-package", "symbol-value", "symbol-function", "symbol-plist", "make-symbol", "copy-symbol", "gensym", "gentemp", "keywordp", "intern", "unintern", "find-symbol", "import", "shadowing-import", "shadow", "export", "unexport", "use-package", "unuse-package", // Package functions "make-package", "in-package", "find-package", "package-name", "package-nicknames", "rename-package", "package-use-list", "package-used-by-list", "package-shadowing-symbols", "list-all-packages", "delete-package", "find-all-symbols", // I/O functions "read", "read-preserving-whitespace", "read-delimited-list", "read-line", "read-char", "unread-char", "peek-char", "listen", "read-char-no-hang", "clear-input", "read-from-string", "parse-integer", "read-byte", "write", "prin1", "print", "pprint", "princ", "write-to-string", "prin1-to-string", "princ-to-string", "write-char", "write-string", "write-line", "terpri", "fresh-line", "finish-output", "force-output", "clear-output", "write-byte", "format", // Stream functions "input-stream-p", "output-stream-p", "interactive-stream-p", "open-stream-p", "stream-element-type", "streamp", "close", "broadcast-stream-streams", "concatenated-stream-streams", "echo-stream-input-stream", "echo-stream-output-stream", "make-broadcast-stream", "make-concatenated-stream", "make-echo-stream", "make-synonym-stream", "make-two-way-stream", "make-string-input-stream", "make-string-output-stream", "get-output-stream-string", "synonym-stream-symbol", "two-way-stream-input-stream", "two-way-stream-output-stream", // File functions "open", "close", "pathname", "truename", "parse-namestring", "merge-pathnames", "make-pathname", "pathnamep", "pathname-host", "pathname-device", "pathname-directory", "pathname-name", "pathname-type", "pathname-version", "namestring", "file-namestring", "directory-namestring", "host-namestring", "enough-namestring", "user-homedir-pathname", "directory", "probe-file", "ensure-directories-exist", "file-write-date", "file-author", "file-position", "file-length", "file-string-length", "load", "compile-file", "compile-file-pathname", // Control functions "apply", "funcall", "mapcar", "maplist", "mapc", "mapl", "mapcan", "mapcon", "values", "values-list", "constantp", "complement", // Evaluation "eval", "compile", "disassemble", "macro-function", "macroexpand", "macroexpand-1", "proclaim", "get-setf-expansion", "documentation", // Environment "apropos", "apropos-list", "describe", "inspect", "dribble", "ed", "lisp-implementation-type", "lisp-implementation-version", "machine-instance", "machine-type", "machine-version", "room", "software-type", "software-version", "short-site-name", "long-site-name", // Time functions "get-decoded-time", "get-universal-time", "decode-universal-time", "encode-universal-time", "get-internal-real-time", "get-internal-run-time", "sleep", // Error handling "error", "cerror", "warn", "signal", "break", "invoke-debugger", // Type functions "coerce", "type-of", "upgraded-array-element-type", "upgraded-complex-part-type" ] ] let contains: [Mode] = [ // Comments Mode(scope: "comment", begin: ";", end: "\n"), // Block comments (some Lisp dialects) Mode(scope: "comment", begin: "#\\|", end: "\\|#"), // Shebang Mode(scope: "comment", begin: "^#!", end: "\n"), // Keywords (symbols starting with :) Mode(scope: "meta", begin: ":[a-zA-Z][a-zA-Z0-9-+*/_<>=!?]*"), // Quoted symbols Mode(scope: "meta", begin: "'[a-zA-Z][a-zA-Z0-9-+*/_<>=!?]*"), // Symbols Mode(scope: "meta", begin: "\\b[a-zA-Z][a-zA-Z0-9-+*/_<>=!?]*\\b"), // Strings CommonModes.stringDouble, // Character literals Mode(scope: "string", begin: "#\\\\(?:[a-zA-Z][a-zA-Z0-9-]*|.)"), // Numbers // Ratio Mode(scope: "number", begin: "[+-]?\\d+/\\d+"), // Complex Mode(scope: "number", begin: "#[cC]\\([+-]?\\d+(?:\\.\\d+)?\\s+[+-]?\\d+(?:\\.\\d+)?\\)"), // Float with exponent Mode(scope: "number", begin: "[+-]?\\d+(?:\\.\\d+)?[eEsSfFdDlL][+-]?\\d+"), // Float Mode(scope: "number", begin: "[+-]?\\d+\\.\\d+"), // Hex Mode(scope: "number", begin: "#[xX][0-9a-fA-F]+"), // Octal Mode(scope: "number", begin: "#[oO][0-7]+"), // Binary Mode(scope: "number", begin: "#[bB][01]+"), // Radix notation (base 2-36) Mode(scope: "number", begin: "#\\d+[rR][0-9a-zA-Z]+"), // Integer Mode(scope: "number", begin: "[+-]?\\d+"), // Arrays/vectors Mode(scope: "meta", begin: "#\\("), Mode(scope: "meta", begin: "#\\d*[aA]"), // Bit vectors Mode(scope: "meta", begin: "#\\*[01]*"), // Backquote and unquote Mode(scope: "keyword", begin: "`"), Mode(scope: "keyword", begin: ",@?"), // Function shorthand Mode(scope: "keyword", begin: "#'"), // Parentheses (for structure) Mode(scope: "keyword", begin: "\\("), Mode(scope: "keyword", begin: "\\)"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Lua.swift ================================================ // // LuaLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct LuaLanguage: LanguageDefinition { let name = "Lua" let aliases: [String]? = ["lua"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while" ], "literal": ["true", "false", "nil"], "built_in": [ // Basic functions "assert", "collectgarbage", "dofile", "error", "getmetatable", "setmetatable", "ipairs", "pairs", "load", "loadfile", "next", "pcall", "xpcall", "print", "rawequal", "rawget", "rawset", "rawlen", "select", "tonumber", "tostring", "type", "warn", // Lua 5.2+ "require", "package", "_G", "_VERSION", // String library "string", "byte", "char", "dump", "find", "format", "gmatch", "gsub", "len", "lower", "match", "pack", "packsize", "rep", "reverse", "sub", "unpack", "upper", // String methods (can be called as string:method) "string.byte", "string.char", "string.dump", "string.find", "string.format", "string.gmatch", "string.gsub", "string.len", "string.lower", "string.match", "string.pack", "string.packsize", "string.rep", "string.reverse", "string.sub", "string.unpack", "string.upper", // Table library "table", "concat", "insert", "move", "pack", "remove", "sort", "unpack", "table.concat", "table.insert", "table.move", "table.pack", "table.remove", "table.sort", "table.unpack", // Math library "math", "abs", "acos", "asin", "atan", "atan2", "ceil", "cos", "cosh", "deg", "exp", "floor", "fmod", "frexp", "huge", "ldexp", "log", "log10", "max", "maxinteger", "min", "mininteger", "modf", "pi", "pow", "rad", "random", "randomseed", "sin", "sinh", "sqrt", "tan", "tanh", "tointeger", "type", "ult", "math.abs", "math.acos", "math.asin", "math.atan", "math.atan2", "math.ceil", "math.cos", "math.cosh", "math.deg", "math.exp", "math.floor", "math.fmod", "math.frexp", "math.huge", "math.ldexp", "math.log", "math.log10", "math.max", "math.maxinteger", "math.min", "math.mininteger", "math.modf", "math.pi", "math.pow", "math.rad", "math.random", "math.randomseed", "math.sin", "math.sinh", "math.sqrt", "math.tan", "math.tanh", "math.tointeger", "math.type", "math.ult", // IO library "io", "close", "flush", "input", "lines", "open", "output", "popen", "read", "tmpfile", "write", "stdin", "stdout", "stderr", "io.close", "io.flush", "io.input", "io.lines", "io.open", "io.output", "io.popen", "io.read", "io.tmpfile", "io.type", "io.write", "io.stdin", "io.stdout", "io.stderr", // File methods "file:close", "file:flush", "file:lines", "file:read", "file:seek", "file:setvbuf", "file:write", // OS library "os", "clock", "date", "difftime", "execute", "exit", "getenv", "remove", "rename", "setlocale", "time", "tmpname", "os.clock", "os.date", "os.difftime", "os.execute", "os.exit", "os.getenv", "os.remove", "os.rename", "os.setlocale", "os.time", "os.tmpname", // Debug library "debug", "gethook", "getinfo", "getlocal", "getmetatable", "getregistry", "getupvalue", "getuservalue", "sethook", "setlocal", "setmetatable", "setupvalue", "setuservalue", "traceback", "upvalueid", "upvaluejoin", "debug.debug", "debug.gethook", "debug.getinfo", "debug.getlocal", "debug.getmetatable", "debug.getregistry", "debug.getupvalue", "debug.getuservalue", "debug.sethook", "debug.setlocal", "debug.setmetatable", "debug.setupvalue", "debug.setuservalue", "debug.traceback", "debug.upvalueid", "debug.upvaluejoin", // Package library "package", "config", "cpath", "loaded", "loadlib", "path", "preload", "searchers", "searchpath", "package.config", "package.cpath", "package.loaded", "package.loadlib", "package.path", "package.preload", "package.searchers", "package.searchpath", // Coroutine library "coroutine", "create", "isyieldable", "resume", "running", "status", "wrap", "yield", "coroutine.create", "coroutine.isyieldable", "coroutine.resume", "coroutine.running", "coroutine.status", "coroutine.wrap", "coroutine.yield", // UTF8 library (Lua 5.3+) "utf8", "char", "charpattern", "codes", "codepoint", "len", "offset", "utf8.char", "utf8.charpattern", "utf8.codes", "utf8.codepoint", "utf8.len", "utf8.offset", // Bitwise operations (Lua 5.3+) "bit32", "arshift", "band", "bnot", "bor", "btest", "bxor", "extract", "lrotate", "lshift", "replace", "rrotate", "rshift", // Metatables and metamethods "__index", "__newindex", "__call", "__concat", "__unm", "__add", "__sub", "__mul", "__div", "__idiv", "__mod", "__pow", "__eq", "__lt", "__le", "__tostring", "__metatable", "__gc", "__mode", "__len", "__pairs", "__ipairs", "__close", "__name", // Special variables "_ENV", "_G", "_VERSION", "arg", // Common Lua patterns "self", "module", "export" ] ] let contains: [Mode] = [ // Multi-line comments (block comments) Mode(scope: "comment", begin: "--\\[\\[", end: "\\]\\]"), Mode(scope: "comment", begin: "--\\[=\\[", end: "\\]=\\]"), Mode(scope: "comment", begin: "--\\[==\\[", end: "\\]==\\]"), // Single-line comments Mode(scope: "comment", begin: "--", end: "\n"), // Shebang Mode(scope: "comment", begin: "^#!", end: "\n"), // Function definitions Mode(scope: "function", begin: "\\bfunction\\s+(?:[a-zA-Z_][a-zA-Z0-9_]*[.:])?([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "function", begin: "\\blocal\\s+function\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), // Multi-line strings (long brackets) Mode(scope: "string", begin: "\\[\\[", end: "\\]\\]"), Mode(scope: "string", begin: "\\[=\\[", end: "\\]=\\]"), Mode(scope: "string", begin: "\\[==\\[", end: "\\]==\\]"), Mode(scope: "string", begin: "\\[===\\[", end: "\\]===\\]"), // Regular strings Mode(scope: "string", begin: "\"", end: "\""), Mode(scope: "string", begin: "'", end: "'"), // Numbers // Hex float (Lua 5.2+) Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+(?:\\.[0-9a-fA-F]+)?(?:[pP][+-]?[0-9]+)?\\b"), // Hex integer Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+\\b"), // Float with exponent Mode(scope: "number", begin: "\\b\\d+\\.?\\d*[eE][+-]?\\d+\\b"), // Float Mode(scope: "number", begin: "\\b\\d+\\.\\d+\\b"), // Integer Mode(scope: "number", begin: "\\b\\d+\\b"), // Labels (goto targets) - Lua 5.2+ Mode(scope: "meta", begin: "::[a-zA-Z_][a-zA-Z0-9_]*::"), // Table constructors (highlighting braces) Mode(scope: "meta", begin: "\\{", end: "\\}"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Matlab.swift ================================================ // // MatlabLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct MatlabLanguage: LanguageDefinition { let name = "MATLAB" let aliases: [String]? = ["matlab", "m"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "break", "case", "catch", "classdef", "continue", "else", "elseif", "end", "for", "function", "global", "if", "otherwise", "parfor", "persistent", "return", "spmd", "switch", "try", "while", // Additional keywords "arguments", "properties", "methods", "events", "enumeration" ], "literal": ["true", "false", "inf", "Inf", "nan", "NaN", "eps", "pi"], "built_in": [ // Basic functions "abs", "acos", "acosh", "acot", "acoth", "acsc", "acsch", "angle", "asec", "asech", "asin", "asinh", "atan", "atan2", "atanh", "ceil", "complex", "conj", "cos", "cosh", "cot", "coth", "csc", "csch", "exp", "fix", "floor", "imag", "isreal", "log", "log10", "log2", "real", "round", "sec", "sech", "sign", "sin", "sinh", "sqrt", "tan", "tanh", "mod", "rem", "gcd", "lcm", // Matrix operations "det", "eig", "inv", "pinv", "rank", "trace", "norm", "cond", "rcond", "expm", "logm", "sqrtm", "chol", "lu", "qr", "svd", "schur", "hess", "balance", "kron", "cross", "dot", // Array operations "size", "length", "numel", "ndims", "isempty", "isequal", "isequaln", "zeros", "ones", "eye", "rand", "randn", "randi", "true", "false", "diag", "tril", "triu", "reshape", "repmat", "cat", "horzcat", "vertcat", "transpose", "ctranspose", "permute", "ipermute", "circshift", "flip", "fliplr", "flipud", "rot90", "squeeze", "shiftdim", // Indexing and sorting "find", "sort", "sortrows", "issorted", "unique", "union", "intersect", "setdiff", "setxor", "ismember", "max", "min", "sum", "prod", "mean", "median", "mode", "std", "var", "cov", "corrcoef", // Logical operations "all", "any", "and", "or", "not", "xor", "isnan", "isinf", "isfinite", "logical", "bitand", "bitor", "bitxor", "bitcmp", "bitshift", "bitget", "bitset", // String operations "char", "string", "strcat", "strcmp", "strcmpi", "strncmp", "strncmpi", "strfind", "strrep", "strsplit", "strjoin", "strip", "lower", "upper", "sprintf", "fprintf", "sscanf", "num2str", "str2num", "str2double", "int2str", "mat2str", "blanks", "deblank", "strtrim", "pad", "strip", // Type conversion and testing "double", "single", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "cast", "typecast", "class", "isa", "isnumeric", "ischar", "isstring", "islogical", "iscell", "isstruct", "istable", "ismatrix", "isvector", "isscalar", "isrow", "iscolumn", // Cell and structure arrays "cell", "cell2mat", "cellstr", "num2cell", "mat2cell", "cellfun", "celldisp", "struct", "struct2cell", "fieldnames", "isfield", "rmfield", "orderfields", "structfun", // File I/O "load", "save", "importdata", "readmatrix", "readtable", "readcell", "writematrix", "writetable", "writecell", "xlsread", "xlswrite", "csvread", "csvwrite", "dlmread", "dlmwrite", "textscan", "fopen", "fclose", "fread", "fwrite", "fprintf", "fscanf", "fgetl", "fgets", "frewind", "fseek", "ftell", "feof", "ferror", // Plotting "plot", "plot3", "scatter", "scatter3", "bar", "barh", "histogram", "pie", "pie3", "surf", "mesh", "contour", "contourf", "imagesc", "image", "imshow", "pcolor", "quiver", "quiver3", "streamline", "fill", "fill3", "area", "stem", "stem3", "stairs", "errorbar", "polarplot", "compass", "feather", "comet", "comet3", // Plot annotation and formatting "title", "xlabel", "ylabel", "zlabel", "legend", "colorbar", "colormap", "grid", "box", "axis", "xlim", "ylim", "zlim", "hold", "subplot", "figure", "clf", "cla", "close", "axes", "gca", "gcf", "set", "get", "text", "annotation", "line", "patch", "rectangle", "saveas", "print", // Statistical functions "histcounts", "histogram", "cumsum", "cumprod", "cummax", "cummin", "movsum", "movmean", "movmedian", "movmax", "movmin", "movstd", "movvar", "diff", "gradient", "del2", "trapz", "cumtrapz", // Interpolation and curve fitting "interp1", "interp2", "interp3", "interpn", "griddedInterpolant", "scatteredInterpolant", "polyfit", "polyval", "polyvalm", "polyder", "polyint", "spline", "pchip", "makima", // Linear algebra "linsolve", "mldivide", "mrdivide", "lsqr", "gmres", "bicg", "cgs", "minres", "pcg", "symmlq", "tfqmr", "null", "orth", "rref", "subspace", // Optimization "fminbnd", "fminsearch", "fzero", "fsolve", "lsqnonlin", "lsqcurvefit", // ODE solvers "ode45", "ode23", "ode113", "ode15s", "ode23s", "ode23t", "ode23tb", "ode15i", "deval", "odeset", "odeget", // FFT and signal processing "fft", "fft2", "fftn", "ifft", "ifft2", "ifftn", "fftshift", "ifftshift", "conv", "conv2", "filter", "deconv", "xcorr", "xcorr2", "corrcoef", // Random number generation "rand", "randn", "randi", "randperm", "rng", // System and environment "pwd", "cd", "dir", "ls", "mkdir", "rmdir", "delete", "copyfile", "movefile", "exist", "which", "what", "path", "addpath", "rmpath", "matlabroot", "version", "computer", "ispc", "isunix", "ismac", // Program control "input", "disp", "display", "warning", "error", "assert", "pause", "keyboard", "dbstop", "dbclear", "dbcont", "dbstep", "dbquit", "diary", "echo", "eval", "evalc", "evalin", "assignin", "run", // Time and date "now", "date", "datenum", "datestr", "datevec", "datetime", "duration", "tic", "toc", "cputime", "clock", "etime", // Table operations "table", "array2table", "cell2table", "struct2table", "table2array", "table2cell", "table2struct", "readtable", "writetable", "join", "innerjoin", "outerjoin", "sortrows", "unique", "ismember", // Miscellaneous "nargin", "nargout", "varargin", "varargout", "inputname", "mfilename", "clc", "clear", "who", "whos", "help", "doc", "lookfor", "demo", "format", "beep", "profile", "bench", "timeit", "tic", "toc" ] ] let contains: [Mode] = [ // Block comments Mode(scope: "comment", begin: "%\\{", end: "%\\}"), // Line comments Mode(scope: "comment", begin: "%", end: "\n"), // Function definitions Mode(scope: "function", begin: "\\bfunction\\s+(?:\\[?[^\\]]*\\]?\\s*=\\s*)?([a-zA-Z_][a-zA-Z0-9_]*)"), // Class definitions Mode(scope: "class", begin: "\\bclassdef\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), // String with single quotes (MATLAB standard) CommonModes.stringSingle, // String with double quotes (newer MATLAB versions) CommonModes.stringDouble, // Transpose operator (should not be confused with string end) Mode(scope: "keyword", begin: "\\.?'(?!')"), // Numbers // Complex numbers with i or j suffix Mode(scope: "number", begin: "\\b\\d+\\.?\\d*[eE][+-]?\\d+[ij]?\\b"), Mode(scope: "number", begin: "\\b\\d+\\.\\d+[ij]?\\b"), Mode(scope: "number", begin: "\\b\\d+[ij]\\b"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+\\b"), // Binary Mode(scope: "number", begin: "\\b0[bB][01]+\\b"), // Regular numbers Mode(scope: "number", begin: "\\b\\d+\\.?\\d*[eE][+-]?\\d+\\b"), Mode(scope: "number", begin: "\\b\\d+\\.\\d+\\b"), Mode(scope: "number", begin: "\\b\\d+\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Mermaid.swift ================================================ // // MermaidLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct MermaidLanguage: LanguageDefinition { let name = "Mermaid" let aliases: [String]? = ["mermaid", "mmd"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ // Diagram types "graph", "flowchart", "sequenceDiagram", "classDiagram", "stateDiagram", "stateDiagram-v2", "erDiagram", "journey", "gantt", "pie", "quadrantChart", "requirementDiagram", "gitGraph", "mindmap", "timeline", "zenuml", "sankey-beta", "xyChart", // Flowchart/Graph directions "TB", "TD", "BT", "RL", "LR", // Flowchart node types "subgraph", "end", // Sequence diagram "participant", "actor", "activate", "deactivate", "note", "loop", "alt", "else", "opt", "par", "and", "rect", "autonumber", "over", "right of", "left of", "link", "links", // Class diagram "class", "namespace", "direction", "link", "click", "callback", "cssClass", "style", // State diagram "state", "note right of", "note left of", "fork", "join", "choice", "concurrency", // ER diagram "entity", "relationship", // Gantt "title", "dateFormat", "axisFormat", "todayMarker", "excludes", "includes", "section", "after", "active", "done", "crit", "milestone", // Git graph "commit", "branch", "checkout", "merge", "reset", "cherry-pick", "tag", "type", "id", "msg", "REVERSE", "HIGHLIGHT", // Journey "title", "section", // Pie "title", "showData", // Requirement diagram "requirement", "functionalRequirement", "interfaceRequirement", "performanceRequirement", "physicalRequirement", "designConstraint", "element", "contains", "copies", "derives", "satisfies", "verifies", "refines", "traces", // Styling "classDef", "class", "style", "linkStyle", "fill", "stroke", "stroke-width", "color", "stroke-dasharray", // Configuration "%%{init:", "theme", "themeVariables", "logLevel", "securityLevel", "startOnLoad", "arrowMarkerAbsolute", "flowchart", "sequence", "gantt", // Quadrant chart "x-axis", "y-axis", "quadrant-1", "quadrant-2", "quadrant-3", "quadrant-4", // Timeline "title", "section", // XY Chart "x-axis", "y-axis", "line", "bar" ], "literal": ["true", "false", "null"], "built_in": [ // Arrow types for flowchart "-->", "---", "-.->", "-.-", "==>", "===", "~~>", "~~~", // Arrow types with text "-- text -->", "-. text .->", "== text ==>", // Sequence diagram arrows "->>", "-->>", "->", "-->", "-x", "--x", "-)", "--)", "<<->>", "<<-->>", // Class relationships "<|--", "*--", "o--", "-->", "..|>", "..", "<--", "--|>", // State transitions "-->", ":-->", // ER relationships "||--o{", "}o--||", "||--|{", "}|--||", "o{--||", "||--{o", "||..o{", "}o..||", "||..|{", "}|..||", "o{..||", "||..{o", // Link text modifiers "|text|", "---|text|", "-->|text|", // Node shapes (flowchart) "()", "[]", "[()]", "[[]]", "[()]", "[([])]", "((()))", ">]", "{}", "{{}}", "[//]", "[\\\\]", "[/\\]", "[\\/]", // Special characters in text "#quot;", "#9829;", "#9830;", "#9827;", "#9824;", "#nbsp;", "#lt;", "#gt;", // Styling keywords "fill", "stroke", "stroke-width", "color", "fill-opacity", "stroke-opacity", "class", "cssClass", "style", "classDef", "linkStyle", // Themes "default", "forest", "dark", "neutral", "base", // Security levels "strict", "loose", "antiscript", "sandbox", // Log levels "debug", "info", "warn", "error", "fatal", // Git graph types "NORMAL", "REVERSE", "HIGHLIGHT", // Journey scores "1", "2", "3", "4", "5", // ER cardinality "zero or one", "one or more", "one or many", "zero or more", "only one", "zero or many", // Class visibility "+", "-", "#", "~", // Class annotations "<>", "<>", "<>", "<>", "<>", "<>", "<>", // State types "[*]", "<>", "<>", "<>", // Time formats "YYYY-MM-DD", "HH:mm", "HH:mm:ss", "HH:mm:ss.SSS", // Axis formats "%Y-%m-%d", "%H:%M", "%H:%M:%S", "%d/%m", "%d/%m/%Y", // Actions "click", "call", "href", "tooltip", // Common properties "id", "title", "description", "type", "risk", "verifyMethod" ] ] let contains: [Mode] = [ // Comments Mode(scope: "comment", begin: "%%", end: "\n"), // Block comments (JSON-style config) Mode(scope: "comment", begin: "%%\\{", end: "\\}%%"), // Diagram type declaration Mode(scope: "keyword", begin: "^\\s*(?:graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|stateDiagram-v2|erDiagram|journey|gantt|pie|quadrantChart|requirementDiagram|gitGraph|mindmap|timeline|zenuml|sankey-beta|xyChart)\\b"), // Subgraph Mode(scope: "class", begin: "\\bsubgraph\\s+([a-zA-Z0-9_]+)"), // Node IDs (various formats) Mode(scope: "meta", begin: "\\b[a-zA-Z][a-zA-Z0-9_-]*\\b"), // Strings with double quotes CommonModes.stringDouble, // Strings with single quotes (for labels) CommonModes.stringSingle, // Strings with backticks (for special characters) Mode(scope: "string", begin: "`", end: "`"), // Node text in square brackets Mode(scope: "string", begin: "\\[", end: "\\]"), // Node text in round brackets Mode(scope: "string", begin: "\\(", end: "\\)"), // Node text in curly brackets Mode(scope: "string", begin: "\\{", end: "\\}"), // Arrow types Mode(scope: "keyword", begin: "(?:-->|---|-.->|-.-|==>|===|~~>|~~~|->>|-->>|->|-->|-x|--x|-\\)|--\\)|<<->>|<<-->>)"), // Class relationships Mode(scope: "keyword", begin: "(?:<\\|--|\\*--|o--|--|\\.\\.\\|>|\\.\\.|<--|--\\|>)"), // ER relationships Mode(scope: "keyword", begin: "(?:\\|\\|--o\\{|\\}o--\\|\\||\\|\\|--\\|\\{|\\}\\|--\\|\\||o\\{--\\|\\||\\|\\|--\\{o|\\|\\|\\.\\.o\\{|\\}o\\.\\.\\|\\||\\|\\|\\.\\.|\\{|\\}\\|\\.\\.|\\||o\\{\\.\\.|\\||\\|\\.\\.|\\{o)"), // State transitions Mode(scope: "keyword", begin: "(?:-->|:-->)"), // Link text (pipe separated) Mode(scope: "string", begin: "\\|", end: "\\|"), // CSS class assignment Mode(scope: "meta", begin: ":::"), // Style definitions Mode(scope: "function", begin: "\\bstyle\\s+[a-zA-Z0-9_-]+"), Mode(scope: "function", begin: "\\bclassDef\\s+[a-zA-Z0-9_-]+"), Mode(scope: "function", begin: "\\blinkStyle\\s+\\d+"), // Click/callback Mode(scope: "function", begin: "\\b(?:click|call)\\s+[a-zA-Z0-9_-]+"), // Participants/Actors Mode(scope: "class", begin: "\\b(?:participant|actor)\\s+([a-zA-Z0-9_]+)"), // Class names Mode(scope: "class", begin: "\\bclass\\s+([a-zA-Z0-9_]+)"), // State names Mode(scope: "class", begin: "\\bstate\\s+(?:\"[^\"]*\"|[a-zA-Z0-9_]+)"), // Numbers Mode(scope: "number", begin: "\\b\\d+(?:\\.\\d+)?\\b"), // Percentages (for pie charts) Mode(scope: "number", begin: "\\b\\d+(?:\\.\\d+)?%"), // Dates (for Gantt) Mode(scope: "string", begin: "\\d{4}-\\d{2}-\\d{2}"), // Colors (hex) Mode(scope: "number", begin: "#[0-9a-fA-F]{3,6}\\b"), // Colors (rgb/rgba) Mode(scope: "function", begin: "rgba?\\("), // URLs Mode(scope: "string", begin: "https?://[^\\s]+"), // Special state markers Mode(scope: "keyword", begin: "\\[\\*\\]"), // Section headers Mode(scope: "class", begin: "^\\s*section\\s+.+$"), // Title Mode(scope: "class", begin: "^\\s*title\\s+.+$"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/ObjectiveC.swift ================================================ // // ObjectiveCLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct ObjectiveCLanguage: LanguageDefinition { let name = "Objective-C" let aliases: [String]? = ["objc", "objective-c", "objectivec", "obj-c", "m", "mm"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ // C keywords "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", // Objective-C keywords "@interface", "@implementation", "@protocol", "@end", "@private", "@protected", "@public", "@package", "@property", "@synthesize", "@dynamic", "@class", "@selector", "@encode", "@defs", "@synchronized", "@try", "@catch", "@throw", "@finally", "@autoreleasepool", "@optional", "@required", // Memory management "retain", "release", "autorelease", "dealloc", "alloc", "init", "copy", "mutableCopy", // Modern Objective-C "strong", "weak", "unsafe_unretained", "assign", "nonatomic", "atomic", "readonly", "readwrite", "getter", "setter", "nullable", "nonnull", "null_resettable", "null_unspecified", "_Nullable", "_Nonnull", "_Null_unspecified", // Special "self", "super", "_cmd", "id", "Class", "SEL", "IMP", "BOOL", "YES", "NO", "nil", "Nil", "NULL", // Type qualifiers "in", "out", "inout", "bycopy", "byref", "oneway", // Blocks "__block", "__weak", "__strong", "__unsafe_unretained", "__autoreleasing", // ARC "__bridge", "__bridge_retained", "__bridge_transfer", // Availability "__deprecated", "__unavailable", "__attribute__", // Other "instancetype", "typeof", "__typeof__" ], "literal": ["YES", "NO", "nil", "Nil", "NULL", "true", "false"], "built_in": [ // Foundation - Basic types "NSObject", "NSString", "NSMutableString", "NSNumber", "NSValue", "NSData", "NSMutableData", "NSDate", "NSCalendar", "NSDateComponents", "NSTimeZone", "NSLocale", "NSURL", "NSURLComponents", "NSURLRequest", "NSMutableURLRequest", "NSURLResponse", "NSHTTPURLResponse", "NSURLSession", "NSURLSessionTask", "NSURLSessionDataTask", "NSURLSessionDownloadTask", "NSURLSessionUploadTask", // Foundation - Collections "NSArray", "NSMutableArray", "NSDictionary", "NSMutableDictionary", "NSSet", "NSMutableSet", "NSOrderedSet", "NSMutableOrderedSet", "NSIndexSet", "NSMutableIndexSet", "NSEnumerator", "NSFastEnumeration", "NSIndexPath", "NSPointerArray", "NSHashTable", "NSMapTable", // Foundation - Text processing "NSAttributedString", "NSMutableAttributedString", "NSScanner", "NSCharacterSet", "NSMutableCharacterSet", "NSRegularExpression", "NSTextCheckingResult", "NSDataDetector", "NSFormatter", "NSNumberFormatter", "NSDateFormatter", "NSByteCountFormatter", "NSLengthFormatter", "NSMassFormatter", "NSEnergyFormatter", // Foundation - Errors "NSError", "NSException", "NSAssertionHandler", // Foundation - File system "NSFileManager", "NSFileHandle", "NSFileWrapper", "NSBundle", "NSProcessInfo", "NSUserDefaults", "NSNotificationCenter", "NSNotification", "NSNotificationQueue", // Foundation - Threading "NSThread", "NSRunLoop", "NSOperation", "NSOperationQueue", "NSBlockOperation", "NSInvocationOperation", "NSCondition", "NSConditionLock", "NSLock", "NSRecursiveLock", "NSDistributedLock", // Foundation - Run loop "NSTimer", "NSPort", "NSPortMessage", // Foundation - Archiving "NSKeyedArchiver", "NSKeyedUnarchiver", "NSCoder", "NSArchiver", "NSUnarchiver", "NSPropertyListSerialization", // Foundation - JSON "NSJSONSerialization", "NSJSONReadingOptions", "NSJSONWritingOptions", // Foundation - KVO/KVC "NSKeyValueObserving", "NSKeyValueCoding", // Foundation - Predicates "NSPredicate", "NSCompoundPredicate", "NSComparisonPredicate", "NSExpression", // Foundation - Sort descriptors "NSSortDescriptor", // Foundation - UUID "NSUUID", // Foundation - Other "NSNull", "NSProxy", "NSInvocation", "NSMethodSignature", "NSUndoManager", "NSCache", "NSPurgeableData", "NSProgress", // UIKit - View Controllers "UIViewController", "UINavigationController", "UITabBarController", "UITableViewController", "UICollectionViewController", "UISplitViewController", "UIPageViewController", "UISearchController", "UIAlertController", "UIActivityViewController", "UIPopoverPresentationController", // UIKit - Views "UIView", "UIWindow", "UILabel", "UIButton", "UITextField", "UITextView", "UIImageView", "UIScrollView", "UITableView", "UICollectionView", "UIPickerView", "UIDatePicker", "UISwitch", "UISlider", "UIStepper", "UISegmentedControl", "UIProgressView", "UIActivityIndicatorView", "UIWebView", "WKWebView", "UIStackView", "UIVisualEffectView", "UIToolbar", "UINavigationBar", "UITabBar", "UISearchBar", "UIPageControl", "UIRefreshControl", // UIKit - Cells "UITableViewCell", "UICollectionViewCell", "UITableViewHeaderFooterView", // UIKit - Layout "NSLayoutConstraint", "NSLayoutAnchor", "UILayoutGuide", "UIEdgeInsets", "CGRect", "CGPoint", "CGSize", "CGFloat", "CGAffineTransform", // UIKit - Graphics "UIColor", "UIImage", "UIFont", "UIBezierPath", "CALayer", "CAShapeLayer", "CAGradientLayer", "CATextLayer", "CAScrollLayer", "CAReplicatorLayer", "CATransformLayer", "CAEmitterLayer", "CATiledLayer", // UIKit - Gestures "UIGestureRecognizer", "UITapGestureRecognizer", "UIPinchGestureRecognizer", "UIRotationGestureRecognizer", "UISwipeGestureRecognizer", "UIPanGestureRecognizer", "UIScreenEdgePanGestureRecognizer", "UILongPressGestureRecognizer", // UIKit - Animation "UIViewPropertyAnimator", "CAAnimation", "CABasicAnimation", "CAKeyframeAnimation", "CAAnimationGroup", "CATransition", "CASpringAnimation", "UIViewAnimating", // UIKit - Other "UIApplication", "UIApplicationDelegate", "UIScreen", "UIDevice", "UIResponder", "UIEvent", "UITouch", "UIPasteboard", "UIMenuController", "UIPrintInteractionController", "UIDocumentInteractionController", "UIDocumentPickerViewController", // Core Graphics "CGContext", "CGPath", "CGImage", "CGColor", "CGColorSpace", "CGGradient", "CGPattern", "CGFont", "CGPDFDocument", "CGPDFPage", // Core Animation "CADisplayLink", "CAMediaTiming", "CAMediaTimingFunction", // Dispatch (GCD) "dispatch_queue_t", "dispatch_group_t", "dispatch_semaphore_t", "dispatch_source_t", "dispatch_block_t", "dispatch_once_t", "dispatch_async", "dispatch_sync", "dispatch_after", "dispatch_once", "dispatch_get_main_queue", "dispatch_get_global_queue", "dispatch_queue_create", "dispatch_group_create", "dispatch_semaphore_create", // Core Data "NSManagedObject", "NSManagedObjectContext", "NSManagedObjectModel", "NSPersistentStoreCoordinator", "NSPersistentStore", "NSFetchRequest", "NSEntityDescription", "NSAttributeDescription", "NSRelationshipDescription", "NSFetchedResultsController", "NSPersistentContainer", // Core Location "CLLocationManager", "CLLocation", "CLLocationCoordinate2D", "CLPlacemark", "CLGeocoder", "CLRegion", "CLCircularRegion", "CLBeaconRegion", "CLHeading", "CLVisit", // MapKit "MKMapView", "MKAnnotation", "MKAnnotationView", "MKPinAnnotationView", "MKPointAnnotation", "MKPolyline", "MKPolygon", "MKCircle", "MKOverlay", "MKOverlayRenderer", "MKDirections", "MKRoute", // AVFoundation "AVPlayer", "AVPlayerItem", "AVPlayerLayer", "AVAsset", "AVURLAsset", "AVAudioPlayer", "AVAudioRecorder", "AVAudioSession", "AVCaptureDevice", "AVCaptureSession", "AVCaptureInput", "AVCaptureOutput", // StoreKit "SKProduct", "SKProductsRequest", "SKPayment", "SKPaymentQueue", "SKPaymentTransaction", "SKStoreProductViewController", // Social/Contacts "CNContact", "CNContactStore", "CNContactPickerViewController", "CNMutableContact", "CNLabeledValue", // UserNotifications "UNUserNotificationCenter", "UNNotificationRequest", "UNNotificationContent", "UNMutableNotificationContent", "UNNotificationTrigger", // Other frameworks "NSLayoutManager", "NSTextContainer", "NSTextStorage", "UICollectionViewLayout", "UICollectionViewFlowLayout" ] ] let contains: [Mode] = [ Mode(scope: "comment", begin: "/\\*", end: "\\*/"), Mode(scope: "comment", begin: "//", end: "\n"), Mode(scope: "meta", begin: "^\\s*#\\s*(?:import|include|define|undef|if|ifdef|ifndef|else|elif|endif|error|pragma|warning)\\b.*$"), Mode(scope: "keyword", begin: "@(?:interface|implementation|protocol|end|class|selector|encode|property|synthesize|dynamic|try|catch|throw|finally|synchronized|autoreleasepool|optional|required)\\b"), Mode(scope: "class", begin: "@interface\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "@protocol\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "@implementation\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "function", begin: "^\\s*[-+]\\s*\\([^)]+\\)\\s*[a-zA-Z_][a-zA-Z0-9_:]*"), // Selectors Mode(scope: "meta", begin: "@selector\\s*\\(", end: "\\)"), // NSString literals Mode(scope: "string", begin: "@\"", end: "\""), // C strings CommonModes.stringDouble, // Character literals Mode(scope: "string", begin: "'(?:[^'\\\\]|\\\\.)+'"), // NSNumber literals Mode(scope: "number", begin: "@(?:\\d+\\.?\\d*|0[xX][0-9a-fA-F]+|YES|NO)\\b"), // Array literals Mode(scope: "meta", begin: "@\\[", end: "\\]"), // Dictionary literals Mode(scope: "meta", begin: "@\\{", end: "\\}"), // Blocks Mode(scope: "function", begin: "\\^\\s*(?:\\([^)]*\\))?\\s*\\{", end: "\\}"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+[uUlL]*\\b"), // Octal Mode(scope: "number", begin: "\\b0[0-7]+[uUlL]*\\b"), // Float/Double Mode(scope: "number", begin: "\\b\\d+\\.\\d+[fFlL]?\\b"), Mode(scope: "number", begin: "\\b\\d+[eE][+-]?\\d+[fFlL]?\\b"), Mode(scope: "number", begin: "\\b\\d+\\.\\d+[eE][+-]?\\d+[fFlL]?\\b"), // Integer Mode(scope: "number", begin: "\\b\\d+[uUlL]*\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Perl.swift ================================================ // // PerlLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct PerlLanguage: LanguageDefinition { let name = "Perl" let aliases: [String]? = ["perl", "pl", "pm"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ // Control flow "if", "elsif", "else", "unless", "given", "when", "default", "while", "until", "for", "foreach", "do", "continue", // Jumps "last", "next", "redo", "goto", // Declarations "sub", "my", "our", "local", "state", // Package/Module "package", "use", "require", "no", // Special "BEGIN", "END", "CHECK", "INIT", "UNITCHECK", // References "ref", "bless", // Operators "and", "or", "not", "xor", "eq", "ne", "lt", "gt", "le", "ge", "cmp", "x", "xx", // Others "return", "undef", "defined", "exists", "delete", "eval", "exec", "die", "warn", "exit", "fork", "wait", "waitpid", "system", "exec", "caller", "wantarray", "prototype", "tied", "untie", // File tests "-r", "-w", "-x", "-o", "-R", "-W", "-X", "-O", "-e", "-z", "-s", "-f", "-d", "-l", "-p", "-S", "-b", "-c", "-t", "-u", "-g", "-k", "-T", "-B", "-M", "-A", "-C", // Special keywords "__FILE__", "__LINE__", "__PACKAGE__", "__SUB__", "__END__", "__DATA__", // Try-catch (Perl 5.34+) "try", "catch", "finally" ], "literal": ["undef"], "built_in": [ // String functions "chomp", "chop", "chr", "crypt", "fc", "hex", "index", "lc", "lcfirst", "length", "oct", "ord", "pack", "reverse", "rindex", "sprintf", "substr", "tr", "uc", "ucfirst", "y", "quotemeta", "split", "join", // Array functions "pop", "push", "shift", "unshift", "splice", "grep", "map", "sort", "reverse", "keys", "values", "each", "delete", "exists", // Hash functions "keys", "values", "each", "exists", "delete", // List functions "grep", "map", "sort", "reverse", // Regex functions "m", "qr", "s", "tr", "y", "match", "split", "pos", "study", // I/O functions "open", "close", "opendir", "closedir", "chdir", "read", "write", "print", "printf", "say", "readline", "readdir", "rewinddir", "seekdir", "telldir", "eof", "fileno", "flock", "select", "getc", "binmode", "sysopen", "sysread", "syswrite", "sysseek", "truncate", "stat", "lstat", "readlink", "symlink", "link", "unlink", "rename", "chmod", "chown", "chroot", "umask", "utime", // File test operators (as functions) "abs", "accept", "alarm", "atan2", "bind", "binmode", "bless", "caller", "chdir", "chmod", "chomp", "chop", "chown", "chr", "chroot", "close", "closedir", "connect", "cos", "crypt", "dbmclose", "dbmopen", "defined", "delete", "die", "do", "dump", "each", "endgrent", "endhostent", "endnetent", "endprotoent", "endpwent", "endservent", "eof", "eval", "exec", "exists", "exit", "exp", "fcntl", "fileno", "flock", "fork", "format", "formline", "getc", "getgrent", "getgrgid", "getgrnam", "gethostbyaddr", "gethostbyname", "gethostent", "getlogin", "getnetbyaddr", "getnetbyname", "getnetent", "getpeername", "getpgrp", "getppid", "getpriority", "getprotobyname", "getprotobynumber", "getprotoent", "getpwent", "getpwnam", "getpwuid", "getservbyname", "getservbyport", "getservent", "getsockname", "getsockopt", "glob", "gmtime", "goto", "grep", "hex", "import", "index", "int", "ioctl", "join", "keys", "kill", "last", "lc", "lcfirst", "length", "link", "listen", "local", "localtime", "lock", "log", "lstat", "map", "mkdir", "msgctl", "msgget", "msgrcv", "msgsnd", "my", "next", "no", "oct", "open", "opendir", "ord", "our", "pack", "package", "pipe", "pop", "pos", "print", "printf", "prototype", "push", "quotemeta", "rand", "read", "readdir", "readline", "readlink", "readpipe", "recv", "redo", "ref", "rename", "require", "reset", "return", "reverse", "rewinddir", "rindex", "rmdir", "say", "scalar", "seek", "seekdir", "select", "semctl", "semget", "semop", "send", "setgrent", "sethostent", "setnetent", "setpgrp", "setpriority", "setprotoent", "setpwent", "setservent", "setsockopt", "shift", "shmctl", "shmget", "shmread", "shmwrite", "shutdown", "sin", "sleep", "socket", "socketpair", "sort", "splice", "split", "sprintf", "sqrt", "srand", "stat", "state", "study", "sub", "substr", "symlink", "syscall", "sysopen", "sysread", "sysseek", "system", "syswrite", "tell", "telldir", "tie", "tied", "time", "times", "tr", "truncate", "uc", "ucfirst", "umask", "undef", "unlink", "unpack", "unshift", "untie", "use", "utime", "values", "vec", "wait", "waitpid", "wantarray", "warn", "write", // Special variables "$_", "@_", "$!", "$@", "$$", "$.", "$,", "$\\", "$\"", "$;", "$#", "$%", "$=", "$-", "$~", "$^", "$:", "$?", "$0", "$ARGV", "$a", "$b", "%ENV", "@ARGV", "@INC", "%INC", "%SIG", "$STDIN", "$STDOUT", "$STDERR", "$^O", "$^X", "$]", "$^V", // Pragmas "strict", "warnings", "utf8", "vars", "subs", "constant", "integer", "locale", "bytes", "open", "less", "feature", "experimental", "autodie", "autouse", "base", "bigint", "bignum", "bigrat", "blib", "diagnostics", "encoding", "fields", "filetest", "if", "lib", "mro", "ops", "overload", "overloading", "parent", "re", "sigtrap", "sort", "threads", "vmsish", // Common modules "Carp", "Data::Dumper", "Exporter", "File::Basename", "File::Copy", "File::Find", "File::Path", "File::Spec", "Getopt::Long", "Getopt::Std", "IO::File", "IO::Handle", "IO::Socket", "List::Util", "POSIX", "Scalar::Util", "Storable", "Time::HiRes", "Time::Local", // Object-oriented "bless", "DESTROY", "AUTOLOAD", "can", "isa", "VERSION", // Modern Perl features "state", "say", "given", "when", "default", "break", "__SUB__" ] ] let contains: [Mode] = [ // POD (Plain Old Documentation) Mode(scope: "comment.doc", begin: "^=\\w+", end: "^=cut"), // Comments Mode(scope: "comment", begin: "#", end: "\n"), // Shebang Mode(scope: "comment", begin: "^#!", end: "\n"), // Subroutine definitions Mode(scope: "function", begin: "\\bsub\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), // Package declaration Mode(scope: "class", begin: "\\bpackage\\s+([a-zA-Z_][a-zA-Z0-9_:]*)"), // Regular expressions (with various delimiters) // m// or // Mode(scope: "string", begin: "\\b(?:m|qr)/", end: "/[gimosxaludn]*"), // m{} Mode(scope: "string", begin: "\\b(?:m|qr)\\{", end: "\\}[gimosxaludn]*"), // m[] Mode(scope: "string", begin: "\\b(?:m|qr)\\[", end: "\\][gimosxaludn]*"), // m() Mode(scope: "string", begin: "\\b(?:m|qr)\\(", end: "\\)[gimosxaludn]*"), // Bare // Mode(scope: "string", begin: "/(?![*/])", end: "/[gimosxaludn]*"), // Substitution s/// Mode(scope: "string", begin: "\\bs/", end: "/[gimosxaludn]*"), Mode(scope: "string", begin: "\\bs\\{", end: "\\}[gimosxaludn]*"), Mode(scope: "string", begin: "\\bs\\[", end: "\\][gimosxaludn]*"), Mode(scope: "string", begin: "\\bs\\(", end: "\\)[gimosxaludn]*"), // Transliteration tr/// or y/// Mode(scope: "string", begin: "\\b(?:tr|y)/", end: "/[cdsr]*"), // Quote-like operators // q{} qq{} qw{} qx{} Mode(scope: "string", begin: "\\bqq?\\{", end: "\\}"), Mode(scope: "string", begin: "\\bqq?\\[", end: "\\]"), Mode(scope: "string", begin: "\\bqq?\\(", end: "\\)"), Mode(scope: "string", begin: "\\bqq?/", end: "/"), Mode(scope: "string", begin: "\\bqw\\{", end: "\\}"), Mode(scope: "string", begin: "\\bqw\\[", end: "\\]"), Mode(scope: "string", begin: "\\bqw\\(", end: "\\)"), Mode(scope: "string", begin: "\\bqx\\{", end: "\\}"), // Here-docs Mode(scope: "string", begin: "<<['\"]?([A-Z_][A-Z0-9_]*)['\"]?", end: "^\\1$"), Mode(scope: "string", begin: "<<~['\"]?([A-Z_][A-Z0-9_]*)['\"]?", end: "^\\s*\\1$"), // Variables Mode(scope: "meta", begin: "[$@%](?:[a-zA-Z_][a-zA-Z0-9_]*|\\{[^}]+\\}|\\^[A-Z]|[0-9]+|[!@#$%^&*()_+=\\[\\]{}|;:,.<>?/~`-])"), // Typeglobs Mode(scope: "meta", begin: "\\*[a-zA-Z_][a-zA-Z0-9_]*"), // Double-quoted strings (with interpolation) Mode(scope: "string", begin: "\"", end: "\""), // Single-quoted strings (no interpolation) CommonModes.stringSingle, // Backtick strings (command execution) Mode(scope: "string", begin: "`", end: "`"), // Numbers // Binary (0b) Mode(scope: "number", begin: "\\b0[bB][01_]+\\b"), // Octal (0 or 0o) Mode(scope: "number", begin: "\\b0[oO]?[0-7_]+\\b"), // Hex (0x) Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F_]+\\b"), // Float with exponent Mode(scope: "number", begin: "\\b\\d[0-9_]*\\.\\d[0-9_]*(?:[eE][+-]?\\d[0-9_]*)?\\b"), Mode(scope: "number", begin: "\\b\\d[0-9_]*[eE][+-]?\\d[0-9_]*\\b"), // Float Mode(scope: "number", begin: "\\b\\d[0-9_]*\\.\\d[0-9_]*\\b"), // Integer with underscores (version numbers) Mode(scope: "number", begin: "\\b\\d[0-9_]*\\b"), // Version strings (v5.10.1) Mode(scope: "number", begin: "\\bv\\d+(?:\\.\\d+)*\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Php.swift ================================================ // // PhpLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 31.08.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct PHPLanguage: LanguageDefinition { let name = "PHP" let aliases: [String]? = ["php"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "abstract","bool", "boolean", "class","final","public","private","protected","static","interface","trait","and","as","break","callable","case","catch","continue","declare","default", "do","double","else","elseif","empty","enddeclare","endfor","endforeach", "endif","endswitch","endwhile","enum","eval","extends","finally","for","foreach", "from","function","global","goto","if","implements","instanceof","insteadof", "int","integer","isset","iterable","list","match","mixed","new","never","object", "or","readonly","real","return","string","switch","throw","try","unset","use", "var","void","while","xor","yield","die","echo","exit","include","include_once", "print","require","require_once", "var_dump" ], "literal": ["true","false","null","TRUE","FALSE","NULL"], "built_in": [ "ArrayAccess","BackedEnum","Closure","Error","AppendIterator","ArgumentCountError","ArithmeticError", "ArrayIterator","ArrayObject","AssertionError","BadFunctionCallException","BadMethodCallException", "CachingIterator","CallbackFilterIterator","CompileError","Countable","DirectoryIterator","DivisionByZeroError", "DomainException","EmptyIterator","ErrorException","Exception","FilesystemIterator","FilterIterator", "GlobIterator","InfiniteIterator","InvalidArgumentException","IteratorIterator","LengthException", "LimitIterator","LogicException","MultipleIterator","NoRewindIterator","OutOfBoundsException", "OutOfRangeException","OuterIterator","OverflowException","ParentIterator","ParseError","RangeException", "RecursiveArrayIterator","RecursiveCachingIterator","RecursiveCallbackFilterIterator","RecursiveDirectoryIterator", "RecursiveFilterIterator","RecursiveIterator","RecursiveIteratorIterator","RecursiveRegexIterator", "RecursiveTreeIterator","RegexIterator","RuntimeException","SeekableIterator","SplDoublyLinkedList", "SplFileInfo","SplFileObject","SplFixedArray","SplHeap","SplMaxHeap","SplMinHeap","SplObjectStorage", "SplObserver","SplPriorityQueue","SplQueue","SplStack","SplSubject","SplTempFileObject","TypeError", "UnderflowException","UnexpectedValueException","UnhandledMatchError","Stringable","Throwable", "Traversable","UnitEnum","WeakReference","WeakMap","Directory","__PHP_Incomplete_Class","parent","php_user_filter", "self","static","stdClass" ] ] let contains: [Mode] = [ Mode(scope: "comment", begin: "/\\*", end: "\\*/"), Mode(scope: "comment", begin: "//[^\n]*"), Mode(scope: "comment", begin: "#[^\n]*"), CommonModes.stringDouble, CommonModes.stringSingle, Mode(scope: "number", begin: "\\b(0[xX][0-9a-fA-F]+|0[bB][01]+|0[oO][0-7]+|\\d+(?:_\\d+)*(?:\\.\\d+(?:_\\d+)*)?(?:[eE][+-]?\\d+)?)\\b"), Mode(scope: "variable", begin: "\\$[a-zA-Z_][a-zA-Z0-9_]*\\b"), Mode(scope: "meta", begin: "<\\?php\\b"), Mode(scope: "meta", begin: "<\\?(?!=\\?)"), Mode(scope: "meta", begin: "\\?>"), Mode(scope: "class", begin: "\\b(?:class|interface|trait)\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "\\bextends\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "\\bimplements\\s+([a-zA-Z_][a-zA-Z0-9_]*(?:\\s*,\\s*[a-zA-Z_][a-zA-Z0-9_]*)*)"), Mode( scope: "function", begin: "\\b(?:fn|function)\\s+([a-zA-Z_][a-zA-Z0-9_]*)" ) ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Python.swift ================================================ // // PythonLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct PythonLanguage: LanguageDefinition { let name = "Python" let aliases: [String]? = ["py", "gyp", "ipython"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "and", "as", "assert", "async", "await", "break", "case", "class", "continue", "def", "del", "elif", "else", "except", "finally", "for", "from", "global", "if", "import", "in", "is", "lambda", "match", "nonlocal", "not", "or", "pass", "raise", "return", "try", "while", "with", "yield" ], "literal": ["True", "False", "None"], "built_in": [ // Встроенные функции "abs", "all", "any", "ascii", "bin", "bool", "breakpoint", "bytearray", "bytes", "callable", "chr", "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod", "enumerate", "eval", "exec", "filter", "float", "format", "frozenset", "getattr", "globals", "hasattr", "hash", "help", "hex", "id", "input", "int", "isinstance", "issubclass", "iter", "len", "list", "locals", "map", "max", "memoryview", "min", "next", "object", "oct", "open", "ord", "pow", "print", "property", "range", "repr", "reversed", "round", "set", "setattr", "slice", "sorted", "staticmethod", "str", "sum", "super", "tuple", "type", "vars", "zip", // Встроенные исключения "BaseException", "Exception", "ArithmeticError", "AssertionError", "AttributeError", "BlockingIOError", "BrokenPipeError", "BufferError", "BytesWarning", "ChildProcessError", "ConnectionError", "EOFError", "EnvironmentError", "FileExistsError", "FileNotFoundError", "FloatingPointError", "FutureWarning", "GeneratorExit", "IOError", "ImportError", "ImportWarning", "IndentationError", "IndexError", "InterruptedError", "IsADirectoryError", "KeyError", "KeyboardInterrupt", "LookupError", "MemoryError", "ModuleNotFoundError", "NameError", "NotADirectoryError", "NotImplementedError", "OSError", "Overflow Error", "PendingDeprecationWarning", "PermissionError", "ProcessLookupError", "RecursionError", "ReferenceError", "ResourceWarning", "RuntimeError", "RuntimeWarning", "StopAsyncIteration", "StopIteration", "SyntaxError", "SyntaxWarning", "SystemError", "SystemExit", "TabError", "TimeoutError", "TypeError", "UnboundLocalError", "UnicodeDecodeError", "UnicodeEncodeError", "UnicodeError", "UnicodeTranslateError", "UnicodeWarning", "UserWarning", "ValueError", "Warning", "ZeroDivisionError", // Специальные "__import__", "__name__", "__doc__", "__file__", "__dict__", "__class__", "self", "cls" ] ] let contains: [Mode] = [ Mode(scope: "comment", begin: "#", end: "\n"), Mode(scope: "meta", begin: "@[a-zA-Z_][a-zA-Z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)*"), Mode(scope: "function", begin: "\\bdef\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "\\bclass\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "string", begin: "\"\"\"", end: "\"\"\""), Mode(scope: "string", begin: "'''", end: "'''"), Mode(scope: "string", begin: "f\"", end: "\""), Mode(scope: "string", begin: "f'", end: "'"), CommonModes.stringDouble, CommonModes.stringSingle, Mode(scope: "string", begin: "r\"(?:[^\"\\\\]|\\\\.)*\""), Mode(scope: "string", begin: "r'(?:[^'\\\\]|\\\\.)*'"), CommonModes.number, Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+\\b"), // Hex Mode(scope: "number", begin: "\\b0[oO][0-7]+\\b"), // Octal Mode(scope: "number", begin: "\\b0[bB][01]+\\b"), // Binary ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/R.swift ================================================ // // RLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct RLanguage: LanguageDefinition { let name = "R" let aliases: [String]? = ["r", "R"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "if", "else", "repeat", "while", "function", "for", "in", "next", "break", "return", "switch", "function", "library", "require", "source" ], "literal": [ "TRUE", "FALSE", "NULL", "NA", "NA_integer_", "NA_real_", "NA_complex_", "NA_character_", "Inf", "NaN" ], "built_in": [ // Base functions "c", "list", "vector", "matrix", "array", "data.frame", "factor", "length", "dim", "nrow", "ncol", "names", "colnames", "rownames", "class", "typeof", "mode", "str", "summary", "head", "tail", "View", // Math functions "abs", "sign", "sqrt", "floor", "ceiling", "trunc", "round", "signif", "exp", "log", "log10", "log2", "sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh", "min", "max", "sum", "prod", "mean", "median", "var", "sd", "range", "quantile", "cumsum", "cumprod", "cummin", "cummax", // Statistical functions "cor", "cov", "lm", "glm", "anova", "aov", "t.test", "chisq.test", "wilcox.test", "fisher.test", "shapiro.test", "ks.test", "var.test", "prop.test", "binom.test", "poisson.test", // Probability distributions "rnorm", "dnorm", "pnorm", "qnorm", "runif", "dunif", "punif", "qunif", "rbinom", "dbinom", "pbinom", "qbinom", "rpois", "dpois", "ppois", "qpois", "rexp", "dexp", "pexp", "qexp", "rgamma", "dgamma", "pgamma", "qgamma", "rbeta", "dbeta", "pbeta", "qbeta", "rt", "dt", "pt", "qt", "rchisq", "dchisq", "pchisq", "qchisq", "rf", "df", "pf", "qf", // Data manipulation "subset", "merge", "aggregate", "apply", "lapply", "sapply", "tapply", "mapply", "vapply", "replicate", "by", "split", "unsplit", "stack", "unstack", "reshape", "transform", "within", "attach", "detach", "with", // Logical functions "all", "any", "which", "which.max", "which.min", "ifelse", // Character functions "paste", "paste0", "cat", "print", "sprintf", "format", "toString", "substr", "substring", "strsplit", "grep", "grepl", "sub", "gsub", "regexpr", "gregexpr", "regmatches", "nchar", "tolower", "toupper", "chartr", "trimws", // Type conversion "as.numeric", "as.integer", "as.logical", "as.character", "as.factor", "as.Date", "as.POSIXct", "as.POSIXlt", "as.matrix", "as.data.frame", "as.list", "as.vector", // Type checking "is.numeric", "is.integer", "is.logical", "is.character", "is.factor", "is.na", "is.null", "is.nan", "is.infinite", "is.finite", "is.matrix", "is.data.frame", "is.list", "is.vector", "is.array", // File I/O "read.csv", "read.table", "read.delim", "readLines", "readRDS", "write.csv", "write.table", "writeLines", "saveRDS", "save", "load", "scan", "file", "open", "close", "readChar", "writeChar", // Data generation "seq", "seq_along", "seq_len", "rep", "rep_len", "gl", "expand.grid", "sample", "set.seed", // Sorting and ordering "sort", "order", "rank", "rev", "unique", "duplicated", "match", // Missing data "na.omit", "na.exclude", "na.fail", "na.pass", "complete.cases", // Graphics (base) "plot", "points", "lines", "abline", "polygon", "rect", "arrows", "hist", "barplot", "boxplot", "pie", "pairs", "matplot", "curve", "par", "layout", "mfrow", "mfcol", "legend", "title", "axis", "grid", "text", "mtext", "points", "segments", "polygon", // Graphics devices "pdf", "png", "jpeg", "tiff", "svg", "dev.new", "dev.off", "dev.cur", "dev.list", "dev.set", // Environment and system "ls", "rm", "exists", "get", "assign", "environment", "parent.frame", "sys.call", "sys.frame", "getwd", "setwd", "dir", "list.files", "file.exists", "file.info", "dir.create", "file.create", "file.remove", "Sys.time", "Sys.Date", "Sys.getenv", "Sys.setenv", "system", "system2", // Package management "install.packages", "library", "require", "loaded.packages", "search", "sessionInfo", // Debugging "debug", "undebug", "browser", "trace", "untrace", "traceback", "stop", "warning", "message", "stopifnot", // Apply family "apply", "lapply", "sapply", "vapply", "mapply", "tapply", "rapply", // Flow control helpers "tryCatch", "try", "withCallingHandlers", "suppressWarnings", "suppressMessages", "invisible", // Special operators "cbind", "rbind", "t", "solve", "det", "eigen", "svd", "qr", "chol", "diag", "crossprod", "tcrossprod", "outer", // Date/Time "Sys.time", "Sys.Date", "as.Date", "strptime", "strftime", "difftime", "ISOdate", "ISOdatetime", // Popular packages functions (commonly used) "ggplot", "aes", "geom_point", "geom_line", "geom_bar", "geom_histogram", "facet_wrap", "facet_grid", "theme", "labs", "scale_x_continuous", "dplyr", "select", "filter", "mutate", "arrange", "group_by", "summarize", "summarise", "left_join", "right_join", "inner_join", "full_join", "tidyr", "gather", "spread", "pivot_longer", "pivot_wider", "data.table", "fread", "fwrite" ] ] let contains: [Mode] = [ // Roxygen comments (documentation) Mode(scope: "comment.doc", begin: "#'", end: "\n"), Mode(scope: "comment", begin: "#", end: "\n"), Mode(scope: "function", begin: "\\b([a-zA-Z_][a-zA-Z0-9._]*)\\s*(?:=|<-)\\s*function"), Mode(scope: "function", begin: "\\b[a-zA-Z_][a-zA-Z0-9._]*\\s*(?=\\()"), // Raw strings (R 4.0+) Mode(scope: "string", begin: "[rR]\"\\(", end: "\\)\""), Mode(scope: "string", begin: "[rR]'\\(", end: "\\)'"), CommonModes.stringDouble, CommonModes.stringSingle, Mode(scope: "string", begin: "`", end: "`"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+[Ll]?\\b"), // Scientific notation Mode(scope: "number", begin: "\\b\\d+\\.?\\d*[eE][+-]?\\d+[Ll]?\\b"), // Float Mode(scope: "number", begin: "\\b\\d+\\.\\d+[Ll]?\\b"), // Integer with L suffix Mode(scope: "number", begin: "\\b\\d+[Ll]\\b"), // Integer Mode(scope: "number", begin: "\\b\\d+\\b"), // Special numeric values Mode(scope: "number", begin: "\\b(?:Inf|NaN)\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Ruby.swift ================================================ // // RubyLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct RubyLanguage: LanguageDefinition { let name = "Ruby" let aliases: [String]? = ["rb", "ruby", "rbw", "rake", "gemspec", "podspec", "thor", "irb"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "alias", "and", "begin", "break", "case", "class", "def", "defined?", "do", "else", "elsif", "end", "ensure", "for", "if", "in", "module", "next", "not", "or", "redo", "rescue", "retry", "return", "self", "super", "then", "undef", "unless", "until", "when", "while", "yield", // Special variables/constants "__FILE__", "__LINE__", "__ENCODING__", // Additional keywords "BEGIN", "END" ], "literal": ["true", "false", "nil"], "built_in": [ // Core classes "Array", "BasicObject", "Binding", "Class", "Comparable", "Complex", "Data", "Dir", "Encoding", "Enumerator", "Enumerable", "ENV", "Exception", "FalseClass", "File", "FileTest", "Float", "GC", "Hash", "Integer", "IO", "Kernel", "Marshal", "MatchData", "Method", "Module", "Mutex", "NilClass", "Numeric", "Object", "ObjectSpace", "Proc", "Process", "Random", "Range", "Rational", "Regexp", "Signal", "String", "Struct", "Symbol", "Thread", "ThreadGroup", "Time", "TrueClass", "UnboundMethod", // Common methods "puts", "print", "printf", "p", "pp", "warn", "raise", "fail", "catch", "throw", "abort", "exit", "exit!", "at_exit", "gets", "readline", "readlines", "chomp", "chomp!", "chop", "chop!", "strip", "strip!", "lstrip", "lstrip!", "rstrip", "rstrip!", "upcase", "upcase!", "downcase", "downcase!", "capitalize", "capitalize!", "swapcase", "swapcase!", "reverse", "reverse!", "concat", "prepend", "insert", "delete", "delete!", "tr", "tr!", "squeeze", "squeeze!", "split", "scan", "match", "sub", "sub!", "gsub", "gsub!", "start_with?", "end_with?", "include?", "index", "rindex", "slice", "slice!", // Array methods "push", "pop", "shift", "unshift", "first", "last", "take", "drop", "each", "each_with_index", "each_index", "map", "collect", "select", "filter", "reject", "find", "detect", "find_all", "any?", "all?", "none?", "one?", "reduce", "inject", "sum", "min", "max", "minmax", "sort", "sort!", "sort_by", "reverse", "reverse!", "flatten", "flatten!", "compact", "compact!", "uniq", "uniq!", "zip", "transpose", "rotate", "rotate!", "sample", "shuffle", "shuffle!", "join", "concat", "length", "size", "count", "empty?", // Hash methods "keys", "values", "has_key?", "key?", "has_value?", "value?", "fetch", "store", "delete", "delete_if", "keep_if", "select!", "reject!", "merge", "merge!", "update", "invert", "to_a", "to_h", "transform_keys", "transform_values", "dig", // Enumerable methods "each_cons", "each_slice", "cycle", "take_while", "drop_while", "group_by", "partition", "chunk", "slice_before", "slice_after", "slice_when", // Numeric methods "abs", "ceil", "floor", "round", "truncate", "to_i", "to_f", "to_s", "to_r", "next", "succ", "pred", "times", "upto", "downto", "step", "even?", "odd?", "zero?", "positive?", "negative?", "finite?", "infinite?", "nan?", // String methods "chars", "bytes", "lines", "codepoints", "bytesize", "encoding", "force_encoding", "encode", "encode!", "intern", "to_sym", "ord", "chr", "center", "ljust", "rjust", "partition", "rpartition", "casecmp", "casecmp?", "hex", "oct", // File/IO methods "open", "read", "write", "close", "closed?", "eof", "eof?", "rewind", "seek", "pos", "tell", "flush", "sync", "binmode", "readlines", "each_line", "getc", "getbyte", "ungetc", "ungetbyte", "sysread", "syswrite", // File class methods "exist?", "exists?", "file?", "directory?", "dirname", "basename", "extname", "expand_path", "absolute_path", "realpath", "join", "split", "chmod", "chown", "delete", "unlink", "rename", "stat", "lstat", "mtime", "atime", "ctime", "size", "size?", // Object methods "class", "is_a?", "kind_of?", "instance_of?", "respond_to?", "methods", "instance_variables", "instance_variable_get", "instance_variable_set", "send", "public_send", "define_method", "define_singleton_method", "method_missing", "const_get", "const_set", "const_defined?", "ancestors", "included_modules", "superclass", "singleton_class", "freeze", "frozen?", "dup", "clone", "taint", "tainted?", "untaint", "trust", "untrust", "untrusted?", "tap", "then", "yield_self", "to_enum", "enum_for", "extend", "include", "prepend", // Kernel methods "require", "require_relative", "load", "autoload", "autoload?", "eval", "exec", "system", "spawn", "syscall", "test", "trap", "caller", "caller_locations", "binding", "block_given?", "iterator?", "lambda", "proc", "loop", "sleep", "rand", "srand", "format", "sprintf", // Comparable "between?", "clamp", // Math "sqrt", "exp", "log", "log10", "log2", "sin", "cos", "tan", "asin", "acos", "atan", "atan2", "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", // Module/Class methods "attr_reader", "attr_writer", "attr_accessor", "attr", "alias_method", "private", "protected", "public", "module_function", "remove_method", "undef_method", "method_defined?", "private_method_defined?", "protected_method_defined?", "public_method_defined?", // Exception classes "StandardError", "RuntimeError", "TypeError", "ArgumentError", "IndexError", "KeyError", "RangeError", "ScriptError", "SyntaxError", "LoadError", "NotImplementedError", "NameError", "NoMethodError", "IOError", "EOFError", "SystemCallError", "ZeroDivisionError", "FloatDomainError", "StopIteration", "LocalJumpError", "SystemExit", "Interrupt", "SignalException" ] ] let contains: [Mode] = [ Mode(scope: "comment.doc", begin: "^=begin", end: "^=end"), Mode(scope: "comment", begin: "#", end: "\n"), // Symbols Mode(scope: "meta", begin: ":[a-zA-Z_][a-zA-Z0-9_]*[!?=]?"), Mode(scope: "meta", begin: ":\"", end: "\""), Mode(scope: "meta", begin: ":'", end: "'"), // Instance variables Mode(scope: "meta", begin: "@[a-zA-Z_][a-zA-Z0-9_]*"), // Class variables Mode(scope: "meta", begin: "@@[a-zA-Z_][a-zA-Z0-9_]*"), // Global variables Mode(scope: "meta", begin: "\\$[a-zA-Z_][a-zA-Z0-9_]*"), Mode(scope: "meta", begin: "\\$[0-9]+"), Mode(scope: "meta", begin: "\\$[!@&`'+~=/\\\\,;.<>*$?:\"]"), Mode(scope: "class", begin: "\\bclass\\s+([A-Z][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "\\bmodule\\s+([A-Z][a-zA-Z0-9_]*)"), // Определение методов Mode(scope: "function", begin: "\\bdef\\s+(?:self\\.)?([a-zA-Z_][a-zA-Z0-9_]*[!?=]?)"), // Percent literals - strings Mode(scope: "string", begin: "%[qQ]?\\{", end: "\\}"), Mode(scope: "string", begin: "%[qQ]?\\[", end: "\\]"), Mode(scope: "string", begin: "%[qQ]?\\(", end: "\\)"), Mode(scope: "string", begin: "%[qQ]?<", end: ">"), Mode(scope: "string", begin: "%[qQ]?\\|", end: "\\|"), Mode(scope: "string", begin: "%[qQ]?/", end: "/"), // Percent literals - arrays Mode(scope: "string", begin: "%[wW]\\{", end: "\\}"), Mode(scope: "string", begin: "%[wW]\\[", end: "\\]"), Mode(scope: "string", begin: "%[wW]\\(", end: "\\)"), // Heredocs Mode(scope: "string", begin: "<<[-~]?['\"]?([A-Z_]+)['\"]?", end: "^\\1$"), // Regular expressions Mode(scope: "string", begin: "/(?![*/])", end: "/[imxo]*"), Mode(scope: "string", begin: "%r\\{", end: "\\}[imxo]*"), Mode(scope: "string", begin: "%r\\[", end: "\\][imxo]*"), Mode(scope: "string", begin: "%r\\(", end: "\\)[imxo]*"), Mode(scope: "string", begin: "%r<", end: ">[imxo]*"), Mode(scope: "string", begin: "%r\\|", end: "\\|[imxo]*"), // String interpolation Mode(scope: "string", begin: "\"", end: "\""), // Single quoted strings (no interpolation) CommonModes.stringSingle, // Backtick strings (command execution) Mode(scope: "string", begin: "`", end: "`"), // Binary Mode(scope: "number", begin: "\\b0[bB][01_]+\\b"), // Octal Mode(scope: "number", begin: "\\b0[oO][0-7_]+\\b"), Mode(scope: "number", begin: "\\b0[0-7_]+\\b"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F_]+\\b"), // Float with exponent Mode(scope: "number", begin: "\\b\\d[0-9_]*\\.\\d[0-9_]*[eE][+-]?\\d[0-9_]*\\b"), Mode(scope: "number", begin: "\\b\\d[0-9_]*[eE][+-]?\\d[0-9_]*\\b"), // Float Mode(scope: "number", begin: "\\b\\d[0-9_]*\\.\\d[0-9_]*\\b"), // Integer with underscores Mode(scope: "number", begin: "\\b\\d[0-9_]*\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Rust.swift ================================================ // // RustLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct RustLanguage: LanguageDefinition { let name = "Rust" let aliases: [String]? = ["rs"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "as", "async", "await", "break", "const", "continue", "crate", "dyn", "else", "enum", "extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct", "super", "trait", "true", "type", "unsafe", "use", "where", "while", // Reserved keywords "abstract", "become", "box", "do", "final", "macro", "override", "priv", "typeof", "unsized", "virtual", "yield", // Edition 2018+ "try" ], "literal": ["true", "false"], "built_in": [ // Primitive types "bool", "char", "str", "i8", "i16", "i32", "i64", "i128", "isize", "u8", "u16", "u32", "u64", "u128", "usize", "f32", "f64", // Common types "String", "Vec", "Box", "Option", "Result", "Some", "None", "Ok", "Err", "HashMap", "HashSet", "BTreeMap", "BTreeSet", "LinkedList", "VecDeque", "BinaryHeap", "Rc", "Arc", "Cell", "RefCell", "Cow", "Mutex", "RwLock", "Path", "PathBuf", "OsString", "OsStr", // Traits "Copy", "Clone", "Debug", "Display", "Default", "Drop", "Eq", "PartialEq", "Ord", "PartialOrd", "Hash", "Iterator", "IntoIterator", "FromIterator", "Extend", "From", "Into", "AsRef", "AsMut", "Deref", "DerefMut", "Add", "Sub", "Mul", "Div", "Rem", "Not", "BitAnd", "BitOr", "BitXor", "Shl", "Shr", "Index", "IndexMut", "Fn", "FnMut", "FnOnce", "Read", "Write", "Seek", "BufRead", "Send", "Sync", "Sized", "Unpin", // Macros "println", "print", "eprintln", "eprint", "format", "panic", "assert", "assert_eq", "assert_ne", "debug_assert", "debug_assert_eq", "debug_assert_ne", "vec", "concat", "include", "include_str", "include_bytes", "env", "option_env", "cfg", "line", "column", "file", "stringify", "module_path", "compile_error", "unimplemented", "unreachable", "todo", "matches", "dbg", "write", "writeln", // Common functions and methods "unwrap", "expect", "unwrap_or", "unwrap_or_else", "unwrap_or_default", "map", "and_then", "or_else", "filter", "collect", "iter", "into_iter", "iter_mut", "len", "is_empty", "push", "pop", "insert", "remove", "clear", "get", "get_mut", "contains", "split", "join", "trim", "to_string", "to_owned", "clone", "chars", "bytes", "lines", "parse", "replace", // std modules "std", "core", "alloc", "collections", "sync", "thread", "io", "fs", "net", "process", "time", "env", "path", "fmt", "mem", "ptr", "slice", "convert", "ops", "cmp", "any", "marker" ] ] let contains: [Mode] = [ // Doc comments Mode(scope: "comment.doc", begin: "///", end: "\n"), Mode(scope: "comment.doc", begin: "//!", end: "\n"), Mode(scope: "comment.doc", begin: "/\\*\\*", end: "\\*/"), Mode(scope: "comment.doc", begin: "/\\*!", end: "\\*/"), Mode(scope: "comment", begin: "/\\*", end: "\\*/"), Mode(scope: "comment", begin: "//", end: "\n"), Mode(scope: "meta", begin: "#!?\\[", end: "\\]"), // Lifetime annotations Mode(scope: "meta", begin: "'[a-zA-Z_][a-zA-Z0-9_]*\\b"), Mode(scope: "function", begin: "\\b[a-zA-Z_][a-zA-Z0-9_]*!"), Mode(scope: "function", begin: "\\bfn\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "\\b(?:struct|enum|trait|type|union)\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), // Raw string literals Mode(scope: "string", begin: "r#+\"", end: "\"#+"), Mode(scope: "string", begin: "r\"", end: "\""), // Byte string literals Mode(scope: "string", begin: "b\"", end: "\""), Mode(scope: "string", begin: "br#+\"", end: "\"#+"), Mode(scope: "string", begin: "br\"", end: "\""), CommonModes.stringDouble, Mode(scope: "string", begin: "'(?:[^'\\\\]|\\\\.)+'"), // Byte literals Mode(scope: "string", begin: "b'(?:[^'\\\\]|\\\\.)+'"), // Binary Mode(scope: "number", begin: "\\b0b[01_]+(?:[ui](?:8|16|32|64|128|size))?\\b"), // Octal Mode(scope: "number", begin: "\\b0o[0-7_]+(?:[ui](?:8|16|32|64|128|size))?\\b"), // Hex Mode(scope: "number", begin: "\\b0x[0-9a-fA-F_]+(?:[ui](?:8|16|32|64|128|size))?\\b"), // Float with exponent Mode(scope: "number", begin: "\\b\\d[0-9_]*(?:\\.[0-9_]+)?[eE][+-]?[0-9_]+(?:f(?:32|64))?\\b"), // Float Mode(scope: "number", begin: "\\b\\d[0-9_]*\\.[0-9_]+(?:f(?:32|64))?\\b"), // Integer with suffix Mode(scope: "number", begin: "\\b\\d[0-9_]*(?:[ui](?:8|16|32|64|128|size)|f(?:32|64))\\b"), // Integer Mode(scope: "number", begin: "\\b\\d[0-9_]*\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Scala.swift ================================================ // // ScalaLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct ScalaLanguage: LanguageDefinition { let name = "Scala" let aliases: [String]? = ["scala", "sc"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "abstract", "case", "catch", "class", "def", "do", "else", "extends", "false", "final", "finally", "for", "forSome", "if", "implicit", "import", "lazy", "match", "new", "null", "object", "override", "package", "private", "protected", "return", "sealed", "super", "this", "throw", "trait", "try", "true", "type", "val", "var", "while", "with", "yield", // Scala 3 keywords "enum", "export", "given", "then", "using", "extension", "inline", "opaque", "open", "transparent", "infix", "end", // Contextual keywords "as", "derives", "macro" ], "literal": ["true", "false", "null"], "built_in": [ // Basic types "Unit", "Boolean", "Byte", "Short", "Int", "Long", "Float", "Double", "Char", "String", "Symbol", "Any", "AnyRef", "AnyVal", "Nothing", "Null", // Collections - immutable "List", "Vector", "Set", "Map", "Seq", "Array", "ArrayBuffer", "IndexedSeq", "LinearSeq", "Queue", "Stack", "Stream", "LazyList", "Range", "NumericRange", "Iterator", "Iterable", "Traversable", "HashSet", "TreeSet", "BitSet", "ListSet", "HashMap", "TreeMap", "ListMap", "LinkedHashMap", "WeakHashMap", // Collections - mutable "ArraySeq", "ListBuffer", "ArrayBuffer", "StringBuilder", "HashSet", "LinkedHashSet", "TreeSet", "BitSet", "HashMap", "LinkedHashMap", "TreeMap", "WeakHashMap", "Queue", "Stack", "PriorityQueue", "ArrayDeque", // Option and Either "Option", "Some", "None", "Either", "Left", "Right", "Try", "Success", "Failure", // Tuple types "Tuple1", "Tuple2", "Tuple3", "Tuple4", "Tuple5", "Tuple6", "Tuple7", "Tuple8", "Tuple9", "Tuple10", "Tuple11", "Tuple12", "Tuple13", "Tuple14", "Tuple15", "Tuple16", "Tuple17", "Tuple18", "Tuple19", "Tuple20", "Tuple21", "Tuple22", // Function types "Function", "Function0", "Function1", "Function2", "Function3", "PartialFunction", "Function22", // Numeric types "BigInt", "BigDecimal", "Numeric", "Integral", "Fractional", "Ordering", "Ordered", // Common methods "map", "flatMap", "filter", "foreach", "fold", "foldLeft", "foldRight", "reduce", "reduceLeft", "reduceRight", "collect", "find", "exists", "forall", "count", "sum", "product", "min", "max", "minBy", "maxBy", "head", "tail", "headOption", "last", "lastOption", "init", "take", "drop", "takeWhile", "dropWhile", "slice", "splitAt", "span", "partition", "groupBy", "grouped", "sliding", "zip", "zipWithIndex", "unzip", "flatten", "distinct", "sorted", "sortBy", "sortWith", "reverse", "reverseMap", "contains", "containsSlice", "corresponds", "startsWith", "endsWith", "indexWhere", "lastIndexWhere", "indexOf", "lastIndexOf", "isEmpty", "nonEmpty", "size", "length", "mkString", // String methods "toUpperCase", "toLowerCase", "trim", "split", "replace", "replaceAll", "replaceFirst", "matches", "substring", "charAt", "concat", // Conversion methods "toList", "toVector", "toSet", "toMap", "toSeq", "toArray", "toStream", "toIterator", "toIndexedSeq", "toBuffer", "toString", "toInt", "toLong", "toDouble", "toFloat", "toBoolean", "toByte", "toShort", "toChar", // Concurrency "Future", "Promise", "Await", "ExecutionContext", "Executor", "Actor", "ActorRef", "ActorSystem", "Props", "Receive", // IO "Source", "BufferedSource", "Codec", "File", "Path", "URL", "URI", // Implicit conversions "implicitly", "Predef", // Scala objects "App", "Array", "Console", "List", "Nil", "StringContext", // Type classes "Numeric", "Ordering", "Equiv", "Manifest", "ClassTag", "TypeTag", "WeakTypeTag", // Reflection "reflect", "ClassTag", "TypeTag", "Mirror", "Universe", // XML (built-in) "Elem", "Node", "NodeSeq", "Text", "XML", // Common traits "Serializable", "Cloneable", "Product", "Equals", // Math "Math", "abs", "min", "max", "sqrt", "pow", "exp", "log", "log10", "sin", "cos", "tan", "asin", "acos", "atan", "atan2", "sinh", "cosh", "tanh", "ceil", "floor", "round", "signum", "random", // Exceptions "Exception", "RuntimeException", "Throwable", "Error", "IllegalArgumentException", "IllegalStateException", "IndexOutOfBoundsException", "NoSuchElementException", "NullPointerException", "ClassCastException", "NumberFormatException", "UnsupportedOperationException", "ArithmeticException", "MatchError", "NotImplementedError", // Annotations "deprecated", "inline", "native", "specialized", "tailrec", "throws", "transient", "unchecked", "volatile", "SerialVersionUID", "annotation", "implicitNotFound", "implicitAmbiguous", // Scala 3 specific "CanEqual", "Matchable", "Singleton", "Selectable", // Common patterns "unapply", "apply", "update", "equals", "hashCode", "toString", "clone", "finalize", "getClass", "notify", "notifyAll", "wait", // Builder pattern "Builder", "CanBuildFrom", "IterableFactory", // Parallel collections "par", "ParSeq", "ParSet", "ParMap", "ParIterable" ] ] let contains: [Mode] = [ // Scaladoc comments Mode(scope: "comment.doc", begin: "/\\*\\*", end: "\\*/"), Mode(scope: "comment", begin: "/\\*", end: "\\*/"), Mode(scope: "comment", begin: "//", end: "\n"), Mode(scope: "meta", begin: "@[a-zA-Z_][a-zA-Z0-9_]*"), Mode(scope: "meta", begin: "'[a-zA-Z_][a-zA-Z0-9_]*\\b"), Mode(scope: "class", begin: "\\b(?:class|object|trait|enum|case class|case object)\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "class", begin: "\\bpackage\\s+([a-zA-Z_][a-zA-Z0-9_.]*)"), Mode(scope: "function", begin: "\\bdef\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), Mode(scope: "meta", begin: "\\b(?:val|var)\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), // String interpolation Mode(scope: "string", begin: "s\"", end: "\""), Mode(scope: "string", begin: "f\"", end: "\""), // Raw strings Mode(scope: "string", begin: "raw\"", end: "\""), // Triple-quoted strings (multiline) Mode(scope: "string", begin: "\"\"\"", end: "\"\"\""), CommonModes.stringDouble, Mode(scope: "string", begin: "'(?:[^'\\\\]|\\\\.)'"), // Hex Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+[lLfFdD]?\\b"), // Binary (Scala 2.13+) Mode(scope: "number", begin: "\\b0[bB][01]+[lL]?\\b"), // Octal Mode(scope: "number", begin: "\\b0[0-7]+[lL]?\\b"), // Float/Double with suffixes Mode(scope: "number", begin: "\\b\\d+\\.\\d+(?:[eE][+-]?\\d+)?[fFdD]?\\b"), Mode(scope: "number", begin: "\\b\\d+[eE][+-]?\\d+[fFdD]?\\b"), // Integer with suffix Mode(scope: "number", begin: "\\b\\d+[lLfFdD]\\b"), // Integer Mode(scope: "number", begin: "\\b\\d+\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Scratch.swift ================================================ // // ScratchLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct ScratchLanguage: LanguageDefinition { let name = "Scratch" let aliases: [String]? = ["scratch", "sb3"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ // Motion blocks "move", "steps", "turn", "right", "left", "degrees", "go", "to", "goto", "random", "position", "glide", "secs", "point", "in", "direction", "towards", "mouse-pointer", "change", "x", "by", "y", "set", "if", "on", "edge", "bounce", "rotation", "style", "don't", "rotate", "left-right", "all", "around", // Looks blocks "say", "for", "think", "show", "hide", "switch", "costume", "next", "backdrop", "size", "effect", "clear", "graphic", "effects", // Sound blocks "start", "sound", "play", "until", "done", "stop", "all", "sounds", "volume", "pitch", "pan", "left", "right", // Events blocks "when", "green", "flag", "clicked", "key", "pressed", "sprite", "stage", "backdrop", "switches", "loudness", "timer", "greater", "than", "broadcast", "message", "wait", "receive", // Control blocks "forever", "repeat", "times", "else", "stop", "this", "script", "other", "scripts", "everything", "clone", "create", "myself", "delete", // Sensing blocks "touching", "color", "distance", "ask", "answer", "down", "username", "current", "year", "month", "date", "day", "of", "week", "hour", "minute", "second", "days", "since", "2000", // Operators blocks "mod", "round", "abs", "floor", "ceiling", "sqrt", "sin", "cos", "tan", "asin", "acos", "atan", "ln", "log", "pow", "join", "letter", "length", "contains", // Variables blocks "make", "variable", "list", "add", "item", "insert", "at", "replace", "with", "contains", "show", "hide", // My Blocks (custom blocks) "define", "run", "without", "screen", "refresh" ], "literal": ["true", "false"], "built_in": [ // Motion reporters "x position", "y position", "direction", // Looks reporters "costume number", "costume name", "backdrop number", "backdrop name", "size", // Sound reporters "volume", // Sensing reporters "answer", "loudness", "timer", "username", "mouse x", "mouse y", "mouse down", // Operators "abs", "floor", "ceiling", "sqrt", "sin", "cos", "tan", "asin", "acos", "atan", "ln", "log", "e ^", "10 ^", "round", "mod", "pick random", "join", "letter of", "length of", "contains", "mathop", // Data "item of", "length", "item #", "contains", // Special values "mouse-pointer", "random position", "edge", // Backdrops and costumes "next backdrop", "previous backdrop", "random backdrop", "next costume", "previous costume", // Effects "color", "fisheye", "whirl", "pixelate", "mosaic", "brightness", "ghost", // Sound effects "pitch", "pan left/right", // Keys "space", "up arrow", "down arrow", "right arrow", "left arrow", "any", "enter", // Special sprites "Stage", "Sprite1", // Pen (extension) "pen down", "pen up", "set pen color", "change pen size", "set pen size", "stamp", "erase all", // Music (extension) "play drum", "rest", "play note", "set instrument", "set tempo", "change tempo", // Text to Speech (extension) "speak", "set voice", "set language", // Translate (extension) "translate", "language", // Video Sensing (extension) "video", "motion", "when motion", // Makey Makey (extension) "when pressed", // LEGO (extension) "motor", "turn on", "turn off", // micro:bit (extension) "display", "button" ] ] let contains: [Mode] = [ Mode(scope: "comment", begin: "//", end: "\n"), Mode(scope: "comment", begin: "/\\*", end: "\\*/"), Mode(scope: "function", begin: "\\b(?:define|when|forever|repeat|if|else)\\b"), Mode(scope: "meta", begin: "\\b[a-zA-Z_][a-zA-Z0-9_]*\\b(?=\\s*(?:=|<-))"), CommonModes.stringDouble, CommonModes.stringSingle, // Float Mode(scope: "number", begin: "\\b\\d+\\.\\d+\\b"), // Integer Mode(scope: "number", begin: "\\b\\d+\\b"), // Negative numbers Mode(scope: "number", begin: "-\\d+\\.?\\d*\\b"), Mode(scope: "literal", begin: "\\b(?:true|false)\\b"), Mode(scope: "keyword", begin: "(?:and|or|not|<|>|=)"), Mode(scope: "number", begin: "#[0-9a-fA-F]{6}\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Shell.swift ================================================ // // ShellLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct ShellLanguage: LanguageDefinition { let name = "Shell" let aliases: [String]? = ["sh", "shell", "zsh", "ksh", "dash"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ // Control flow "if", "then", "else", "elif", "fi", "case", "esac", "for", "select", "while", "until", "do", "done", "in", "function", "time", // Declarations "declare", "typeset", "local", "export", "readonly", "unset", // Built-in commands "break", "continue", "return", "exit", "shift", "eval", "exec", "source", ".", "trap", "wait", "jobs", "bg", "fg", "disown", "suspend", "alias", "unalias", "set", "unset", "shopt", "enable", "command", "builtin", "caller", "true", "false", // Test commands "test", "[", "[[", // Compound commands "{", "}", "((", "))", "[[", "]]" ], "literal": ["true", "false"], "built_in": [ // File operations "cat", "cp", "mv", "rm", "rmdir", "mkdir", "touch", "ln", "chmod", "chown", "chgrp", "ls", "pwd", "cd", "pushd", "popd", "dirs", "find", "locate", "which", "whereis", "file", "stat", "du", "df", "mount", "umount", "dd", "tar", "gzip", "gunzip", "bzip2", "bunzip2", "zip", "unzip", "compress", "uncompress", "rsync", "scp", "sftp", // Text processing "echo", "printf", "read", "cat", "head", "tail", "less", "more", "grep", "egrep", "fgrep", "sed", "awk", "cut", "paste", "join", "sort", "uniq", "wc", "tr", "expand", "unexpand", "fold", "fmt", "nl", "pr", "tee", "split", "csplit", "diff", "patch", "cmp", "comm", "column", "iconv", "dos2unix", "unix2dos", // Process management "ps", "top", "htop", "kill", "killall", "pkill", "pgrep", "pidof", "nice", "renice", "nohup", "screen", "tmux", "at", "batch", "cron", "crontab", "sleep", "timeout", "watch", "xargs", // System information "uname", "hostname", "uptime", "who", "whoami", "id", "groups", "users", "last", "lastlog", "w", "finger", "date", "cal", "time", "timedatectl", "localectl", "hostnamectl", // Network "ping", "traceroute", "netstat", "ss", "ip", "ifconfig", "route", "arp", "dig", "nslookup", "host", "wget", "curl", "nc", "netcat", "telnet", "ftp", "ssh", "scp", "rsync", "nmap", "tcpdump", // User management "useradd", "usermod", "userdel", "groupadd", "groupmod", "groupdel", "passwd", "chpasswd", "su", "sudo", "visudo", // Package management "apt", "apt-get", "aptitude", "dpkg", "yum", "dnf", "rpm", "zypper", "pacman", "brew", "snap", "flatpak", // System management "systemctl", "service", "journalctl", "dmesg", "shutdown", "reboot", "poweroff", "halt", "init", "telinit", // Shell built-ins "alias", "bg", "bind", "builtin", "caller", "cd", "command", "compgen", "complete", "compopt", "continue", "declare", "dirs", "disown", "echo", "enable", "eval", "exec", "exit", "export", "false", "fc", "fg", "getopts", "hash", "help", "history", "jobs", "kill", "let", "local", "logout", "mapfile", "popd", "printf", "pushd", "pwd", "read", "readarray", "readonly", "return", "set", "shift", "shopt", "source", "suspend", "test", "times", "trap", "true", "type", "typeset", "ulimit", "umask", "unalias", "unset", "wait", // Common utilities "basename", "dirname", "expr", "bc", "dc", "seq", "yes", "tty", "stty", "clear", "reset", "script", "rev", "factor", "env", "printenv", "getopt", "getopts", "mktemp", "mkfifo", "tput", // Archiving "tar", "cpio", "zip", "unzip", "gzip", "gunzip", "bzip2", "bunzip2", "xz", "unxz", "7z", "rar", "unrar", // Disk operations "fdisk", "parted", "mkfs", "fsck", "tune2fs", "resize2fs", "blkid", "lsblk", "hdparm", "smartctl", // Variables "PATH", "HOME", "USER", "SHELL", "PWD", "OLDPWD", "TMPDIR", "LANG", "LC_ALL", "TERM", "EDITOR", "VISUAL", "PAGER", "PS1", "PS2", "PS3", "PS4", "IFS", "RANDOM", "SECONDS", "LINENO", "BASHPID", "BASH_VERSION", "HOSTNAME", "UID", "EUID", "GROUPS", "PPID", "SHLVL", "BASH_SUBSHELL", // Special parameters "$@", "$*", "$#", "$$", "$!", "$?", "$-", "$_", "$0", // Test operators "-e", "-f", "-d", "-L", "-h", "-b", "-c", "-p", "-S", "-t", "-r", "-w", "-x", "-s", "-u", "-g", "-k", "-O", "-G", "-N", "-nt", "-ot", "-ef", "-z", "-n", "=", "!=", "==", "-eq", "-ne", "-lt", "-le", "-gt", "-ge", "&&", "||", "!" ] ] let contains: [Mode] = [ // Shebang Mode(scope: "comment", begin: "^#!", end: "\n"), // Comments Mode(scope: "comment", begin: "#", end: "\n"), // Heredoc Mode(scope: "string", begin: "<<-?\\s*(['\"]?)([a-zA-Z_][a-zA-Z0-9_]*)\\1", end: "^\\2$"), // Variables Mode(scope: "meta", begin: "\\$[a-zA-Z_][a-zA-Z0-9_]*"), Mode(scope: "meta", begin: "\\$\\{[^}]+\\}"), Mode(scope: "meta", begin: "\\$\\([^)]+\\)"), Mode(scope: "meta", begin: "\\$\\(\\([^)]+\\)\\)"), // Special variables Mode(scope: "meta", begin: "\\$[0-9@*#?$!_-]"), // Command substitution (backticks) Mode(scope: "string", begin: "`", end: "`"), // Strings with double quotes (allows variable expansion) Mode(scope: "string", begin: "\"", end: "\""), // Strings with single quotes (no expansion) CommonModes.stringSingle, // ANSI-C quoting Mode(scope: "string", begin: "\\$'", end: "'"), // Functions Mode(scope: "function", begin: "^\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(\\s*\\)"), Mode(scope: "function", begin: "\\bfunction\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), // Numbers Mode(scope: "number", begin: "\\b0[xX][0-9a-fA-F]+\\b"), Mode(scope: "number", begin: "\\b0[0-7]+\\b"), Mode(scope: "number", begin: "\\b[0-9]+\\b"), // Redirection operators Mode(scope: "keyword", begin: "[0-9]*(?:>>|>|<<|<|&>|&>>|<&|>&|<>)"), // Pipe Mode(scope: "keyword", begin: "\\|\\|?|&&?|;|&"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Sql.swift ================================================ struct SQLLanguage: LanguageDefinition { let name = "SQL" let aliases: [String]? = ["sql", "mysql", "postgresql", "sqlite"] let caseInsensitive = true let keywords: [String: [String]]? = [ "keyword": [ // DDL (Data Definition Language) "CREATE", "ALTER", "DROP", "TRUNCATE", "RENAME", "ADD", "MODIFY", "CHANGE", "COLUMN", "TABLE", "VIEW", "INDEX", "DATABASE", "SCHEMA", // DML (Data Manipulation Language) "SELECT", "INSERT", "UPDATE", "DELETE", "REPLACE", "INTO", "VALUES", "SET", // DQL (Data Query Language) "FROM", "WHERE", "GROUP", "BY", "HAVING", "ORDER", "ASC", "DESC", "LIMIT", "OFFSET", "TOP", // Joins "JOIN", "INNER", "LEFT", "RIGHT", "FULL", "OUTER", "CROSS", "ON", "USING", // Logical operators "AND", "OR", "NOT", "IN", "EXISTS", "BETWEEN", "LIKE", "IS", "NULL", "ISNULL", // Set operations "UNION", "INTERSECT", "EXCEPT", "MINUS", // Subqueries "ALL", "ANY", "SOME", // Constraints "PRIMARY", "KEY", "FOREIGN", "REFERENCES", "UNIQUE", "CHECK", "DEFAULT", "AUTO_INCREMENT", // Transactions "BEGIN", "COMMIT", "ROLLBACK", "TRANSACTION", "SAVEPOINT", // Other "AS", "DISTINCT", "CASE", "WHEN", "THEN", "ELSE", "END", "IF", "ELSEIF", "ENDIF", "WHILE", "LOOP", "REPEAT", "DECLARE", "CURSOR", "FETCH", "CLOSE", "OPEN", "FUNCTION", "PROCEDURE", "DIV", "CALL", "FOR", "CONTINUE", "HANDLER", "LEAVE", "GRANT", "TO", "USER", "IDENTIFIED", "CASCADE", "FOREIGN_KEY_CHECKS", "COLLATE", "CHARSET", "RETURNS" ], "built_in": [ // Aggregate functions "COUNT", "SUM", "AVG", "MIN", "MAX", "GROUP_CONCAT", "STDDEV", "VARIANCE", // String functions "CONCAT", "LENGTH", "SUBSTRING", "SUBSTR", "TRIM", "LTRIM", "RTRIM", "UPPER", "LOWER", "REPLACE", "REVERSE", "LEFT", "RIGHT", "CHARINDEX", "INSTR", "LOCATE", "POSITION", "ASCII", "CHAR", // Date/Time functions "NOW", "CURDATE", "CURTIME", "YEAR", "MONTH", "DAY", "HOUR", "MINUTE", "SECOND", "DATEDIFF", "DATEADD", "DATE_FORMAT", "STR_TO_DATE", // Math functions "ABS", "CEIL", "CEILING", "FLOOR", "ROUND", "TRUNCATE", "MOD", "POWER", "SQRT", "RAND", "PI", "SIN", "COS", "TAN", // Conditional functions "IFNULL", "NULLIF", "COALESCE", // Conversion functions "CAST", "CONVERT", "FORMAT" ], "type": [ // Numeric types "INT", "INTEGER", "SMALLINT", "TINYINT", "MEDIUMINT", "BIGINT", "DECIMAL", "NUMERIC", "FLOAT", "DOUBLE", "REAL", "BIT", "BOOLEAN", "BOOL", "SERIAL", "UNSIGNED", // String types "CHAR", "VARCHAR", "TEXT", "TINYTEXT", "MEDIUMTEXT", "LONGTEXT", "BINARY", "VARBINARY", "BLOB", "TINYBLOB", "MEDIUMBLOB", "LONGBLOB", "ENUM", "SET", // Date/Time types "DATE", "TIME", "DATETIME", "TIMESTAMP", "YEAR", // JSON and other types "JSON", "UUID", "POINT", "GEOMETRY", "LINESTRING", "POLYGON" ], "literal": [ "TRUE", "FALSE", "NULL", "UNKNOWN" ] ] let contains: [Mode] = [ Mode(scope: "comment", begin: "--.*$"), Mode(scope: "comment", begin: "\\#.*$"), Mode(scope: "comment", begin: "/\\*", end: "\\*/"), Mode(scope: "string", begin: "'(?:[^'\\\\]|\\\\.)*'"), Mode(scope: "string", begin: "\"(?:[^\"\\\\]|\\\\.)*\""), Mode(scope: "number", begin: "\\b(?:0[xX][0-9a-fA-F]+|\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?)\\b"), Mode(scope: "variable", begin: "@[a-zA-Z_][a-zA-Z0-9_]*\\b"), Mode(scope: "variable", begin: "@@[a-zA-Z_][a-zA-Z0-9_]*\\b"), Mode(scope: "variable", begin: "\\$[a-zA-Z_][a-zA-Z0-9_]*\\b") , Mode(scope: "class", begin: "`([a-zA-Z_][a-zA-Z0-9_]*)`"), Mode(scope: "function", begin: "\\b([a-zA-Z_][a-zA-Z0-9_]*)(?=\\s*\\()"), Mode(scope: "operator", begin: "\\+|\\-|\\*|/|%|=|!=|<>|<=|>=|<|>|\\|\\||&&") ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/Swift.swift ================================================ // SwiftLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 31.08.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct SwiftLanguage: LanguageDefinition { let name = "Swift" let aliases: [String]? = ["swift"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ "class","struct","enum","protocol","extension","func","init","deinit", "var","let","if","else","switch","case","default","for","while","repeat", "break","continue","return","throw","try","catch","guard","defer","import", "in","as","is","super","self","Type","where","associatedtype","subscript" ], "modifier": [ "public","private","internal","fileprivate","open", "static","final","override","lazy","weak","unowned", "required","convenience","mutating","nonmutating","throws","rethrows" ], "literal": ["true","false","nil","self","super"], "built_in": [ "Array","Dictionary","Set","String","Character","Int","UInt","Double","Float", "Bool","Optional","Result","Error","Any","Never","Void" ] ] let contains: [Mode] = [ CommonModes.comment(begin: "//", end: "\n"), CommonModes.comment(begin: "/\\*", end: "\\*/"), CommonModes.stringDouble, CommonModes.number, Mode( scope: "variable", begin: "\\b(?:let|var)\\s+([a-zA-Z_][a-zA-Z0-9_]*)" ), Mode( scope: "function", begin: "\\bfunc\\s+([a-zA-Z_][a-zA-Z0-9_]*)" ), Mode( scope: "function", begin: "\\binit\\s*(?:\\(|\\s)" ), Mode( scope: "function", begin: "\\bdeinit\\s*(?:\\{|\\s|$)" ), Mode( scope: "class", begin: "\\b(?:class|struct|enum|protocol|extension)\\s+([a-zA-Z_][a-zA-Z0-9_]*)" ), Mode( scope: "class", begin: ":\\s*([a-zA-Z_][a-zA-Z0-9_]*(?:\\s*,\\s*[a-zA-Z_][a-zA-Z0-9_]*)*)" ), Mode( scope: "class", begin: ":\\s*([a-zA-Z_][a-zA-Z0-9_]*(?:<[^>]*>)?(?:\\?|!)?)" ), Mode( scope: "class", begin: "\\bas\\s+([a-zA-Z_][a-zA-Z0-9_]*(?:<[^>]*>)?(?:\\?|!)?)" ), Mode( scope: "class", begin: "\\bis\\s+([a-zA-Z_][a-zA-Z0-9_]*)" ) ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Languages/TypeScript.swift ================================================ // // TypeScriptLanguage.swift // FSNotes // // Created by Oleksandr Hlushchenko on 04.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct TypeScriptLanguage: LanguageDefinition { let name = "TypeScript" let aliases: [String]? = ["typescript", "ts", "tsx"] let caseInsensitive = false let keywords: [String: [String]]? = [ "keyword": [ // JavaScript keywords "as", "async", "await", "break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do", "else", "enum", "export", "extends", "false", "finally", "for", "from", "function", "get", "if", "import", "in", "instanceof", "let", "new", "null", "of", "return", "set", "static", "super", "switch", "this", "throw", "true", "try", "typeof", "var", "void", "while", "with", "yield", // TypeScript specific "abstract", "as", "asserts", "any", "boolean", "constructor", "declare", "get", "infer", "interface", "is", "keyof", "module", "namespace", "never", "readonly", "require", "number", "object", "set", "string", "symbol", "type", "undefined", "unique", "unknown", "from", "global", "bigint", "of", "implements", "private", "protected", "public", // Modifiers "abstract", "async", "const", "declare", "export", "private", "protected", "public", "readonly", "static", "override" ], "literal": ["true", "false", "null", "undefined", "NaN", "Infinity"], "built_in": [ // Primitive types "any", "boolean", "number", "string", "symbol", "void", "undefined", "null", "never", "unknown", "bigint", "object", // Built-in types "Array", "ArrayBuffer", "AsyncIterable", "AsyncIterableIterator", "AsyncIterator", "Atomics", "BigInt", "BigInt64Array", "BigUint64Array", "Boolean", "DataView", "Date", "Error", "EvalError", "Float32Array", "Float64Array", "Function", "Generator", "GeneratorFunction", "Infinity", "Int8Array", "Int16Array", "Int32Array", "Intl", "InternalError", "Iterable", "IterableIterator", "Iterator", "JSON", "Map", "Math", "NaN", "Number", "Object", "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", "Set", "SharedArrayBuffer", "String", "Symbol", "SyntaxError", "TypeError", "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", "URIError", "WeakMap", "WeakSet", "WebAssembly", // Utility types "Partial", "Required", "Readonly", "Record", "Pick", "Omit", "Exclude", "Extract", "NonNullable", "Parameters", "ConstructorParameters", "ReturnType", "InstanceType", "ThisParameterType", "OmitThisParameter", "ThisType", "Uppercase", "Lowercase", "Capitalize", "Uncapitalize", "Awaited", // Global functions "eval", "isFinite", "isNaN", "parseFloat", "parseInt", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "escape", "unescape", // Console "console", "log", "warn", "error", "info", "debug", "trace", "assert", "clear", "count", "countReset", "dir", "dirxml", "group", "groupCollapsed", "groupEnd", "table", "time", "timeEnd", "timeLog", "profile", "profileEnd", // Common methods "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "constructor", // Array methods "concat", "copyWithin", "entries", "every", "fill", "filter", "find", "findIndex", "flat", "flatMap", "forEach", "from", "includes", "indexOf", "isArray", "join", "keys", "lastIndexOf", "map", "of", "pop", "push", "reduce", "reduceRight", "reverse", "shift", "slice", "some", "sort", "splice", "toLocaleString", "toString", "unshift", "values", // String methods "charAt", "charCodeAt", "codePointAt", "concat", "endsWith", "includes", "indexOf", "lastIndexOf", "localeCompare", "match", "matchAll", "normalize", "padEnd", "padStart", "repeat", "replace", "replaceAll", "search", "slice", "split", "startsWith", "substring", "toLowerCase", "toLocaleLowerCase", "toUpperCase", "toLocaleUpperCase", "trim", "trimEnd", "trimStart", // Object methods "assign", "create", "defineProperties", "defineProperty", "entries", "freeze", "fromEntries", "getOwnPropertyDescriptor", "getOwnPropertyDescriptors", "getOwnPropertyNames", "getOwnPropertySymbols", "getPrototypeOf", "is", "isExtensible", "isFrozen", "isSealed", "keys", "preventExtensions", "seal", "setPrototypeOf", "values", // Promise methods "all", "allSettled", "any", "race", "reject", "resolve", "then", "catch", "finally", // Map/Set methods "clear", "delete", "entries", "forEach", "get", "has", "keys", "set", "size", "values", // Math methods "abs", "acos", "acosh", "asin", "asinh", "atan", "atan2", "atanh", "cbrt", "ceil", "clz32", "cos", "cosh", "exp", "expm1", "floor", "fround", "hypot", "imul", "log", "log10", "log1p", "log2", "max", "min", "pow", "random", "round", "sign", "sin", "sinh", "sqrt", "tan", "tanh", "trunc", "E", "LN2", "LN10", "LOG2E", "LOG10E", "PI", "SQRT1_2", "SQRT2", // Number methods "isFinite", "isInteger", "isNaN", "isSafeInteger", "parseFloat", "parseInt", "toExponential", "toFixed", "toPrecision", // JSON methods "parse", "stringify", // Module/namespace "require", "module", "exports", "__dirname", "__filename", "global", // TypeScript globals "NodeJS", "RequestInit", "Response", "Request", "Headers", "FormData", "Blob", "File", "ReadableStream", "WritableStream", "TextEncoder", "TextDecoder", "URL", "URLSearchParams", "Event", "EventTarget", "AbortController", "AbortSignal", // React (for TSX) "React", "Component", "PureComponent", "Fragment", "createElement", "cloneElement", "createContext", "forwardRef", "lazy", "memo", "startTransition", "useCallback", "useContext", "useDebugValue", "useDeferredValue", "useEffect", "useId", "useImperativeHandle", "useInsertionEffect", "useLayoutEffect", "useMemo", "useReducer", "useRef", "useState", "useSyncExternalStore", "useTransition", "JSX", "ReactElement", "ReactNode", "FC", "PropsWithChildren" ] ] let contains: [Mode] = [ // JSDoc comments Mode(scope: "comment.doc", begin: "/\\*\\*", end: "\\*/"), // Multi-line comments Mode(scope: "comment", begin: "/\\*", end: "\\*/"), // Single-line comments Mode(scope: "comment", begin: "//", end: "\n"), // Type annotations Mode(scope: "meta", begin: ":\\s*[a-zA-Z_][a-zA-Z0-9_<>\\[\\]|&,\\s]*"), // Generic type parameters Mode(scope: "meta", begin: "<[a-zA-Z_][a-zA-Z0-9_<>\\[\\]|&,\\s]*>"), // Type assertions Mode(scope: "meta", begin: "\\bas\\s+[a-zA-Z_][a-zA-Z0-9_<>\\[\\]|&]*"), Mode(scope: "meta", begin: "<[a-zA-Z_][a-zA-Z0-9_<>\\[\\]|&]*>(?=\\s*[a-zA-Z_({['\"])"), // Interface/Type definitions Mode(scope: "class", begin: "\\b(?:interface|type|enum|namespace|module|declare)\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), // Class definitions Mode(scope: "class", begin: "\\bclass\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), // Function definitions Mode(scope: "function", begin: "\\bfunction\\s+([a-zA-Z_][a-zA-Z0-9_]*)"), // Arrow functions Mode(scope: "function", begin: "\\([^)]*\\)\\s*=>"), Mode(scope: "function", begin: "[a-zA-Z_][a-zA-Z0-9_]*\\s*=>"), // Method definitions Mode(scope: "function", begin: "\\b[a-zA-Z_][a-zA-Z0-9_]*\\s*\\("), // Decorators Mode(scope: "meta", begin: "@[a-zA-Z_][a-zA-Z0-9_]*"), // Template literals Mode(scope: "string", begin: "`", end: "`"), // Regular strings CommonModes.stringDouble, CommonModes.stringSingle, // Regular expressions Mode(scope: "string", begin: "/(?![*/])", end: "/[gimsuvy]*"), // JSX/TSX tags Mode(scope: "keyword", begin: ""), // Labels (for GoTo) Mode(scope: "meta", begin: "^\\s*[a-zA-Z_][a-zA-Z0-9_]*:"), // Date literals Mode(scope: "string", begin: "#", end: "#"), // Strings with double quotes CommonModes.stringDouble, // Character literals (VB.NET) Mode(scope: "string", begin: "\"[cC]", end: "\""), // Numbers // Hex Mode(scope: "number", begin: "\\b&[hH][0-9a-fA-F]+[sSlL%&!#@]?\\b"), // Octal Mode(scope: "number", begin: "\\b&[oO][0-7]+[sSlL%&!#@]?\\b"), // Binary (VB 14+) Mode(scope: "number", begin: "\\b&[bB][01]+[sSlL%&!#@]?\\b"), // Scientific notation Mode(scope: "number", begin: "\\b\\d+\\.?\\d*[eE][+-]?\\d+[fFdDrR]?\\b"), // Float with type suffix Mode(scope: "number", begin: "\\b\\d+\\.\\d+[fFdDrR]?\\b"), // Integer with type suffix Mode(scope: "number", begin: "\\b\\d+[sSlLiI%&!#@fFdDrR]\\b"), // Regular integer Mode(scope: "number", begin: "\\b\\d+\\b"), ] } ================================================ FILE: FSNotesCore/SwiftHighlighter/Platform.swift ================================================ // // PlatformColor.swift // FSNotes // // Created by Oleksandr Hlushchenko on 14.11.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // #if os(OSX) import AppKit public typealias PlatformFont = NSFont public typealias PlatformColor = NSColor public typealias FontTraits = NSFontDescriptor.SymbolicTraits #else import UIKit public typealias PlatformFont = UIFont public typealias PlatformColor = UIColor public typealias FontTraits = UIFontDescriptor.SymbolicTraits public extension FontTraits { static let bold: FontTraits = .traitBold static let italic: FontTraits = .traitItalic } #endif extension PlatformColor { convenience init(hex: String) { let trimHex = hex.trimmingCharacters(in: .whitespacesAndNewlines) let hexString: String if trimHex.hasPrefix("#") { hexString = String(trimHex.dropFirst()) } else { hexString = trimHex } var value: UInt64 = 0 Scanner(string: hexString).scanHexInt64(&value) let r, g, b, a: CGFloat switch hexString.count { case 6: // RRGGBB r = CGFloat((value >> 16) & 0xFF) / 255 g = CGFloat((value >> 8) & 0xFF) / 255 b = CGFloat(value & 0xFF) / 255 a = 1.0 case 8: // RRGGBBAA r = CGFloat((value >> 24) & 0xFF) / 255 g = CGFloat((value >> 16) & 0xFF) / 255 b = CGFloat((value >> 8) & 0xFF) / 255 a = CGFloat(value & 0xFF) / 255 default: // Некорректный формат — делаем черный цвет r = 0; g = 0; b = 0; a = 1 } self.init(red: r, green: g, blue: b, alpha: a) } public static var label: PlatformColor { #if os(OSX) return NSColor.lightGray #else return UIColor.lightGray #endif } } extension PlatformFont { static func withTraits(font: PlatformFont, traits: FontTraits) -> PlatformFont { #if os(OSX) let manager = NSFontManager.shared var desiredTraits: NSFontTraitMask = [] if traits.contains(.bold) { desiredTraits.insert(.boldFontMask) } if traits.contains(.italic) { desiredTraits.insert(.italicFontMask) } return manager.convert(font, toHaveTrait: desiredTraits) #else if let descriptor = font.fontDescriptor.withSymbolicTraits(traits) { return PlatformFont(descriptor: descriptor, size: font.pointSize) } return font #endif } } ================================================ FILE: FSNotesCore/SwiftHighlighter/SwiftHighlighter.swift ================================================ import Foundation #if os(OSX) import AppKit #else import UIKit #endif // MARK: - Core Types public protocol LanguageDefinition { var name: String { get } var aliases: [String]? { get } var keywords: [String: [String]]? { get } var contains: [Mode] { get } var caseInsensitive: Bool { get } } public struct Mode { var scope: String? var begin: String? var end: String? var keywords: [String: [String]]? var contains: [Mode]? private var _beginRegex: NSRegularExpression? private var _endRegex: NSRegularExpression? public init( scope: String? = nil, begin: String? = nil, end: String? = nil, keywords: [String: [String]]? = nil, contains: [Mode]? = nil ) { self.scope = scope self.begin = begin self.end = end self.keywords = keywords self.contains = contains if let begin = begin { self._beginRegex = try? NSRegularExpression(pattern: begin, options: [.anchorsMatchLines]) } if let end = end { self._endRegex = try? NSRegularExpression(pattern: end, options: [.anchorsMatchLines]) } } var beginRegex: NSRegularExpression? { _beginRegex } var endRegex: NSRegularExpression? { _endRegex } } public struct Match { let range: Range let scope: String let text: String } extension Match { var substring: Substring { text[range] } var nsRange: NSRange { NSRange(range, in: text) } } // MARK: - Highlight Style public struct HighlightStyle { public struct TextStyle { public var color: PlatformColor public var traits: FontTraits = [] } public var font: PlatformFont = PlatformFont.systemFont(ofSize: 14) public var foregroundColor: PlatformColor = .black public var backgroundColor: PlatformColor = .white public var styles: [String: TextStyle] = [:] public init() {} public func attributes(for scope: String) -> [NSAttributedString.Key: Any] { guard let style = styles[scope] else { return [.font: font, .foregroundColor: PlatformColor.label] } let customFont = PlatformFont.withTraits(font: font, traits: style.traits) return [.font: customFont, .foregroundColor: style.color] } } // MARK: - Renderer class AttributedStringRenderer { private let style: HighlightStyle private let baseAttributes: [NSAttributedString.Key: Any] init(style: HighlightStyle = HighlightStyle()) { self.style = style self.baseAttributes = [ .font: style.font, .foregroundColor: PlatformColor.label ] } func render(text: String, matches: [Match]) -> NSAttributedString { let attributedString = NSMutableAttributedString(string: text, attributes: baseAttributes) let sortedMatches = matches.sorted { $0.range.lowerBound < $1.range.lowerBound } for match in sortedMatches { let nsRange = match.nsRange if nsRange.location + nsRange.length <= attributedString.length { attributedString.addAttributes(style.attributes(for: match.scope), range: nsRange) } } return attributedString } func apply(matches: [Match], to textStorage: NSMutableAttributedString, offset: Int) { let sortedMatches = matches.sorted { $0.range.lowerBound < $1.range.lowerBound } for match in sortedMatches { let nsRange = match.nsRange let convertedRange = NSRange(location: offset + nsRange.location, length: nsRange.length) if convertedRange.location + convertedRange.length <= textStorage.length { textStorage.addAttributes(style.attributes(for: match.scope), range: convertedRange) } } } } // MARK: - Swift Highlighter public class SwiftHighlighter { private static var languages: [String: LanguageDefinition] = [:] private var aliases: [String: String] = [:] private var renderer: AttributedStringRenderer public var options: Options public struct Options { public var style: HighlightStyle = HighlightStyle() public static let `default` = Options() public init(style: HighlightStyle = HighlightStyle()) { self.style = style } } public init(options: Options = .default) { self.options = options self.renderer = AttributedStringRenderer(style: options.style) if SwiftHighlighter.languages.isEmpty { self.registerLanguage("swift", definition: SwiftLanguage()) self.registerLanguage("php", definition: PHPLanguage()) self.registerLanguage("javascript", definition: JavaScriptLanguage()) self.registerLanguage("sql", definition: SQLLanguage()) self.registerLanguage("python", definition: PythonLanguage()) self.registerLanguage("c", definition: CLanguage()) self.registerLanguage("cpp", definition: CppLanguage()) self.registerLanguage("java", definition: JavaLanguage()) self.registerLanguage("go", definition: GoLanguage()) self.registerLanguage("rust", definition: RustLanguage()) self.registerLanguage("csharp", definition: CSharpLanguage()) self.registerLanguage("kotlin", definition: KotlinLanguage()) self.registerLanguage("r", definition: RLanguage()) self.registerLanguage("ruby", definition: RubyLanguage()) self.registerLanguage("matlab", definition: MatlabLanguage()) self.registerLanguage("dart", definition: DartLanguage()) self.registerLanguage("vb", definition: VbLanguage()) self.registerLanguage("assembly", definition: AssemblyLanguage()) self.registerLanguage("scratch", definition: ScratchLanguage()) self.registerLanguage("groovy", definition: GroovyLanguage()) self.registerLanguage("objectivec", definition: ObjectiveCLanguage()) self.registerLanguage("scala", definition: ScalaLanguage()) self.registerLanguage("bash", definition: BashLanguage()) self.registerLanguage("haskell", definition: HaskellLanguage()) self.registerLanguage("erlang", definition: ErlangLanguage()) self.registerLanguage("perl", definition: PerlLanguage()) self.registerLanguage("lua", definition: LuaLanguage()) self.registerLanguage("clojure", definition: ClojureLanguage()) self.registerLanguage("html", definition: HTMLLanguage()) self.registerLanguage("css", definition: CSSLanguage()) self.registerLanguage("sh", definition: ShellLanguage()) self.registerLanguage("ts", definition: TypeScriptLanguage()) self.registerLanguage("lisp", definition: LispLanguage()) self.registerLanguage("mermaid", definition: MermaidLanguage()) } } public func getLanguages() -> [String] { Array(SwiftHighlighter.languages.keys) } public func updateStyle(_ style: HighlightStyle) { self.options.style = style self.renderer = AttributedStringRenderer(style: style) } public func registerLanguage(_ name: String, definition: LanguageDefinition) { SwiftHighlighter.languages[name.lowercased()] = definition if let aliases = definition.aliases { for alias in aliases { self.aliases[alias.lowercased()] = name.lowercased() } } } public func getLanguage(_ name: String) -> LanguageDefinition? { let lower = name.lowercased() return SwiftHighlighter.languages[lower] ?? SwiftHighlighter.languages[aliases[lower] ?? ""] } public func highlight(_ code: String, language: String) -> NSAttributedString { guard let langDef = getLanguage(language) else { let baseAttrs: [NSAttributedString.Key: Any] = [ .font: options.style.font, .foregroundColor: PlatformColor.label ] return NSAttributedString(string: code, attributes: baseAttrs) } let matches = processLanguage(langDef, text: code) return renderer.render(text: code, matches: matches) } private func processLanguage(_ language: LanguageDefinition, text: String) -> [Match] { var matches: [Match] = [] var protectedRanges: [Range] = [] for mode in language.contains { let modeMatches = processMode( mode, text: text, searchRange: text.startIndex.., protectedRanges: [Range] = [] ) -> [Match] { var matches: [Match] = [] var currentIndex = searchRange.lowerBound while currentIndex < searchRange.upperBound { guard let regex = mode.beginRegex else { break } let nsSearchRange = NSRange(currentIndex.. 0, let codePlainRange = Range(codeRange, in: attributedString.string) { let codeText = String(attributedString.string[codePlainRange]) let matches = processLanguage(langDefinition, text: codeText) renderer.apply(matches: matches, to: attributedString, offset: codeRange.location) } attributedString.fixAttributes(in: codeRange) // Apply ticks and lang highlighting if shouldHighlightTicks { highlightCodeBlockDelimiters( in: attributedString, range: fullRange, language: language, hasLanguageDefinition: langDefinition != nil ) } } private func calculateCodeRange( fullRange: NSRange, language: String? ) -> NSRange { let codeStartOffset = language.map { 3 + $0.count } ?? 0 return NSRange( location: fullRange.location + codeStartOffset, length: max(0, fullRange.length - codeStartOffset - 3) ) } private func highlightCodeBlockDelimiters( in attributedString: NSMutableAttributedString, range: NSRange, language: String?, hasLanguageDefinition: Bool ) { let grayColor = Color.lightGray let greenColor = Color(red: 0.18, green: 0.61, blue: 0.25, alpha: 1.0) // open ``` let openRange = NSRange(location: range.location, length: 3) attributedString.addAttributes([ .foregroundColor: grayColor, .font: NotesTextProcessor.codeFont ], range: openRange) // lang if hasLanguageDefinition, let language = language, !language.isEmpty { let langRange = NSRange(location: range.location + 3, length: language.count) attributedString.addAttribute(.foregroundColor, value: greenColor, range: langRange) } // close ``` let closeRange = NSRange(location: range.upperBound - 4, length: 4) attributedString.addAttributes([ .foregroundColor: grayColor, .font: NotesTextProcessor.codeFont ], range: closeRange) } private func getLanguage(from attributedString: NSMutableAttributedString, startingAt start: Int) -> String? { let nsString = attributedString.string as NSString guard start + 3 <= nsString.length else { return nil } // Check for ``` guard nsString.character(at: start) == 0x60, nsString.character(at: start + 1) == 0x60, nsString.character(at: start + 2) == 0x60 else { return nil } var end = start + 3 let len = nsString.length // Find whitespace while end < len { let char = nsString.character(at: end) if char == 0x0A || char == 0x20 { break } end += 1 } guard end > start + 3 else { return nil } return nsString.substring(with: NSRange(location: start + 3, length: end - start - 3)) .trimmingCharacters(in: .whitespaces) } private func expandRangeForMultilineConstructs( in attributedString: NSAttributedString, editedRange: NSRange, codeRange: NSRange, language: LanguageDefinition? ) -> NSRange? { guard let language = language else { return nil } let multilineModes = language.contains.filter { $0.begin != nil && $0.end != nil } guard !multilineModes.isEmpty else { return nil } var expandedLocation = editedRange.location var expandedEnd = editedRange.location + editedRange.length for mode in multilineModes { guard let beginRegex = mode.beginRegex, let endRegex = mode.endRegex else { continue } let isPaired = mode.begin == mode.end let allMatches = beginRegex.matches(in: attributedString.string, range: codeRange) if isPaired { var openMatch: NSTextCheckingResult? for match in allMatches { if match.range.location < editedRange.location { openMatch = (openMatch == nil) ? match : nil // toggle } else if openMatch != nil { expandedLocation = min(expandedLocation, openMatch!.range.location) expandedEnd = max(expandedEnd, match.range.location + match.range.length) break } } } else { for beginMatch in allMatches.reversed() { if beginMatch.range.location > editedRange.location + editedRange.length { continue } let searchStart = beginMatch.range.location + beginMatch.range.length let searchEnd = codeRange.location + codeRange.length guard searchEnd > searchStart else { continue } let searchRange = NSRange( location: searchStart, length: searchEnd - searchStart ) if let endMatch = endRegex.firstMatch(in: attributedString.string, range: searchRange) { let endMatchEnd = endMatch.range.location + endMatch.range.length if editedRange.location >= beginMatch.range.location && editedRange.location <= endMatchEnd { expandedLocation = min(expandedLocation, beginMatch.range.location) expandedEnd = max(expandedEnd, endMatchEnd) break } } } } } // Safe expandedLocation = max(expandedLocation, codeRange.location) expandedEnd = min(expandedEnd, codeRange.location + codeRange.length) guard expandedEnd > expandedLocation, expandedLocation != editedRange.location || expandedEnd != editedRange.location + editedRange.length else { return nil } return NSRange(location: expandedLocation, length: expandedEnd - expandedLocation) } } // MARK: - Common Modes public struct CommonModes { public static let stringDouble = Mode(scope: "string", begin: "\"(?:[^\"\\\\]|\\\\.)*\"") public static let stringSingle = Mode(scope: "string", begin: "'(?:[^'\\\\]|\\\\.)*'") public static let number = Mode(scope: "number", begin: "\\b\\d+(?:\\.\\d+)?\\b") public static func comment(begin: String, end: String? = nil) -> Mode { Mode(scope: "comment", begin: begin + (end != nil ? ".*?\(end!)" : ".*$")) } } ================================================ FILE: FSNotesCore/SwiftHighlighter/Theme.swift ================================================ // // Theme.swift // FSNotes // // Created by Oleksandr Hlushchenko on 01.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // enum EditorTheme: String, CaseIterable, Codable { case github case atomOne case solarized init?(themeName: String) { switch themeName.lowercased() { case "github": self = .github case "atom-one": self = .atomOne case "solarized": self = .solarized default: return nil } } func makeStyle(isDark: Bool) -> HighlightStyle { switch (self, isDark) { case (.github, false): return GitHubLightTheme.make() case (.github, true): return GitHubDarkTheme.make() case (.atomOne, false): return AtomOneLightTheme.make() case (.atomOne, true): return AtomOneDarkTheme.make() case (.solarized, false): return SolarizedLightTheme.make() case (.solarized, true): return SolarizedDarkTheme.make() } } func getName() -> String { switch self { case .github: return "github" case .atomOne: return "atom-one" case .solarized: return "solarized" } } func getCssName(isDark: Bool) -> String { switch self { case .github: return "github-" + (isDark ? "dark" : "light") case .atomOne: return "atom-one-" + (isDark ? "dark" : "light") case .solarized: return "solarized-" + (isDark ? "dark" : "light") } } } ================================================ FILE: FSNotesCore/SwiftHighlighter/Themes/AtomOneDark.swift ================================================ // // AtomOneDark.swift // FSNotes // // Created by Oleksandr Hlushchenko on 01.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct AtomOneDarkTheme { static func make() -> HighlightStyle { var style = HighlightStyle() style.font = UserDefaultsManagement.codeFont // .hljs style.foregroundColor = PlatformColor(hex: "#abb2bf") style.backgroundColor = PlatformColor(hex: "#3a3f4a") // .hljs-comment, .hljs-quote (italic) style.styles["comment"] = .init( color: PlatformColor(hex: "#5c6370"), traits: [.italic] ) style.styles["quote"] = .init( color: PlatformColor(hex: "#5c6370"), traits: [.italic] ) // .hljs-doctag, .hljs-keyword, .hljs-formula style.styles["keyword"] = .init(color: PlatformColor(hex: "#c678dd")) style.styles["formula"] = .init(color: PlatformColor(hex: "#c678dd")) // .hljs-section, .hljs-name, .hljs-selector-tag, .hljs-deletion, .hljs-subst style.styles["section"] = .init(color: PlatformColor(hex: "#e06c75")) style.styles["name"] = .init(color: PlatformColor(hex: "#e06c75")) style.styles["tag"] = .init(color: PlatformColor(hex: "#e06c75")) style.styles["deletion"] = .init(color: PlatformColor(hex: "#e06c75")) style.styles["subst"] = .init(color: PlatformColor(hex: "#e06c75")) // .hljs-literal style.styles["literal"] = .init(color: PlatformColor(hex: "#56b6c2")) // .hljs-string, .hljs-regexp, .hljs-addition, .hljs-attribute, .hljs-meta-string style.styles["string"] = .init(color: PlatformColor(hex: "#98c379")) style.styles["regexp"] = .init(color: PlatformColor(hex: "#98c379")) style.styles["addition"] = .init(color: PlatformColor(hex: "#98c379")) style.styles["attribute"] = .init(color: PlatformColor(hex: "#98c379")) // .hljs-built_in, .hljs-class .hljs-title style.styles["built_in"] = .init(color: PlatformColor(hex: "#e6c07b")) style.styles["class"] = .init(color: PlatformColor(hex: "#e6c07b")) // .hljs-attr, .hljs-variable, .hljs-template-variable, // .hljs-type, .hljs-selector-class, .hljs-number style.styles["attr"] = .init(color: PlatformColor(hex: "#d19a66")) style.styles["variable"] = .init(color: PlatformColor(hex: "#d19a66")) style.styles["type"] = .init(color: PlatformColor(hex: "#d19a66")) style.styles["number"] = .init(color: PlatformColor(hex: "#d19a66")) // .hljs-symbol, .hljs-bullet, .hljs-link, .hljs-meta, .hljs-selector-id, .hljs-title style.styles["symbol"] = .init(color: PlatformColor(hex: "#61aeee")) style.styles["bullet"] = .init(color: PlatformColor(hex: "#61aeee")) style.styles["link"] = .init(color: PlatformColor(hex: "#61aeee")) style.styles["meta"] = .init(color: PlatformColor(hex: "#61aeee")) style.styles["id"] = .init(color: PlatformColor(hex: "#61aeee")) style.styles["title"] = .init(color: PlatformColor(hex: "#61aeee")) // emphasis / strong style.styles["emphasis"] = .init( color: PlatformColor(hex: "#e06c75"), traits: [.italic] ) style.styles["strong"] = .init( color: PlatformColor(hex: "#e06c75"), traits: [.bold] ) return style } } ================================================ FILE: FSNotesCore/SwiftHighlighter/Themes/AtomOneLight.swift ================================================ // // AtomOneLight.swift // FSNotes // // Created by Oleksandr Hlushchenko on 01.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct AtomOneLightTheme { static func make() -> HighlightStyle { var style = HighlightStyle() style.font = UserDefaultsManagement.codeFont // .hljs style.foregroundColor = PlatformColor(hex: "#383a42") style.backgroundColor = PlatformColor(hex: "#fafafa") // .hljs-comment, .hljs-quote (italic) style.styles["comment"] = .init( color: PlatformColor(hex: "#a0a1a7"), traits: [.italic] ) style.styles["quote"] = .init( color: PlatformColor(hex: "#a0a1a7"), traits: [.italic] ) // .hljs-doctag, .hljs-keyword, .hljs-formula style.styles["keyword"] = .init(color: PlatformColor(hex: "#a626a4")) style.styles["formula"] = .init(color: PlatformColor(hex: "#a626a4")) // .hljs-section, .hljs-name, .hljs-selector-tag, .hljs-deletion, .hljs-subst style.styles["section"] = .init(color: PlatformColor(hex: "#e45649")) style.styles["name"] = .init(color: PlatformColor(hex: "#e45649")) style.styles["tag"] = .init(color: PlatformColor(hex: "#e45649")) style.styles["deletion"] = .init(color: PlatformColor(hex: "#e45649")) style.styles["subst"] = .init(color: PlatformColor(hex: "#e45649")) // .hljs-literal style.styles["literal"] = .init(color: PlatformColor(hex: "#0184bb")) // .hljs-string, .hljs-regexp, .hljs-addition, .hljs-attribute, .hljs-meta-string style.styles["string"] = .init(color: PlatformColor(hex: "#50a14f")) style.styles["regexp"] = .init(color: PlatformColor(hex: "#50a14f")) style.styles["addition"] = .init(color: PlatformColor(hex: "#50a14f")) style.styles["attribute"] = .init(color: PlatformColor(hex: "#50a14f")) // .hljs-built_in, .hljs-class .hljs-title style.styles["built_in"] = .init(color: PlatformColor(hex: "#c18401")) style.styles["class"] = .init(color: PlatformColor(hex: "#c18401")) // .hljs-attr, .hljs-variable, .hljs-template-variable, // .hljs-type, .hljs-selector-class, .hljs-number style.styles["attr"] = .init(color: PlatformColor(hex: "#986801")) style.styles["variable"] = .init(color: PlatformColor(hex: "#986801")) style.styles["type"] = .init(color: PlatformColor(hex: "#986801")) style.styles["number"] = .init(color: PlatformColor(hex: "#986801")) // .hljs-symbol, .hljs-bullet, .hljs-link, .hljs-meta, .hljs-selector-id, .hljs-title style.styles["symbol"] = .init(color: PlatformColor(hex: "#4078f2")) style.styles["bullet"] = .init(color: PlatformColor(hex: "#4078f2")) style.styles["link"] = .init(color: PlatformColor(hex: "#4078f2")) style.styles["meta"] = .init(color: PlatformColor(hex: "#4078f2")) style.styles["id"] = .init(color: PlatformColor(hex: "#4078f2")) style.styles["title"] = .init(color: PlatformColor(hex: "#4078f2")) // emphasis / strong style.styles["emphasis"] = .init( color: PlatformColor(hex: "#e45649"), traits: [.italic] ) style.styles["strong"] = .init( color: PlatformColor(hex: "#e45649"), traits: [.bold] ) return style } } ================================================ FILE: FSNotesCore/SwiftHighlighter/Themes/GitHubDark.swift ================================================ // // GitHubDark.swift // FSNotes // // Created by Oleksandr Hlushchenko on 01.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct GitHubDarkTheme { static func make() -> HighlightStyle { var style = HighlightStyle() style.font = UserDefaultsManagement.codeFont // .hljs { color, background } style.foregroundColor = PlatformColor(hex: "#F1F1F1") style.backgroundColor = PlatformColor(hex: "#232520") // .hljs-keyword, .hljs-type, .hljs-template-*, .hljs-doctag style.styles["keyword"] = .init(color: PlatformColor(hex: "#ff7b72")) style.styles["type"] = .init(color: PlatformColor(hex: "#ff7b72")) style.styles["modifier"] = .init(color: PlatformColor(hex: "#ff7b72")) // .hljs-title (function, class) style.styles["function"] = .init(color: PlatformColor(hex: "#d2a8ff")) style.styles["class"] = .init(color: PlatformColor(hex: "#d2a8ff")) // .hljs-attr, .hljs-literal, .hljs-number, .hljs-variable, etc style.styles["attribute"] = .init(color: PlatformColor(hex: "#79c0ff")) style.styles["literal"] = .init(color: PlatformColor(hex: "#79c0ff")) style.styles["number"] = .init(color: PlatformColor(hex: "#79c0ff")) style.styles["variable"] = .init(color: PlatformColor(hex: "#79c0ff")) style.styles["operator"] = .init(color: PlatformColor(hex: "#79c0ff")) style.styles["meta"] = .init(color: PlatformColor(hex: "#79c0ff")) // .hljs-string, .hljs-regexp style.styles["string"] = .init(color: PlatformColor(hex: "#a5d6ff")) style.styles["regexp"] = .init(color: PlatformColor(hex: "#a5d6ff")) style.styles["quote"] = .init(color: PlatformColor(hex: "#a5d6ff")) // .hljs-built_in, .hljs-symbol style.styles["built_in"] = .init(color: PlatformColor(hex: "#ffa657")) style.styles["symbol"] = .init(color: PlatformColor(hex: "#ffa657")) // .hljs-comment, .hljs-code, .hljs-formula style.styles["comment"] = .init( color: PlatformColor(hex: "#8b949e"), traits: [.italic] ) // .hljs-name, .hljs-selector-tag style.styles["name"] = .init(color: PlatformColor(hex: "#7ee787")) style.styles["tag"] = .init(color: PlatformColor(hex: "#7ee787")) // .hljs-subst style.styles["subst"] = .init(color: PlatformColor(hex: "#c9d1d9")) // .hljs-section style.styles["section"] = .init( color: PlatformColor(hex: "#1f6feb"), traits: [.bold] ) // .hljs-bullet style.styles["bullet"] = .init(color: PlatformColor(hex: "#f2cc60")) // .hljs-emphasis style.styles["emphasis"] = .init( color: PlatformColor(hex: "#c9d1d9"), traits: [.italic] ) // .hljs-strong style.styles["strong"] = .init( color: PlatformColor(hex: "#c9d1d9"), traits: [.bold] ) // .hljs-addition style.styles["addition"] = .init( color: PlatformColor(hex: "#aff5b4"), ) // .hljs-deletion style.styles["deletion"] = .init( color: PlatformColor(hex: "#ffdcd7"), ) // punctuation / params / property — deliberately default style.styles["punctuation"] = .init(color: PlatformColor(hex: "#79c0ff")) style.styles["params"] = .init(color: PlatformColor(hex: "#79c0ff")) style.styles["property"] = .init(color: PlatformColor(hex: "#79c0ff")) return style } } ================================================ FILE: FSNotesCore/SwiftHighlighter/Themes/GitHubLight.swift ================================================ // // GitHubTheme.swift // FSNotes // // Created by Oleksandr Hlushchenko on 31.08.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // #if os(OSX) import AppKit #else import UIKit #endif struct GitHubLightTheme { static func make() -> HighlightStyle { var style = HighlightStyle() style.font = UserDefaultsManagement.codeFont style.foregroundColor = PlatformColor(hex: "#24292e") style.backgroundColor = PlatformColor(hex: "#ffffff") style.styles["keyword"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#d73a49")) style.styles["doctag"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#d73a49")) style.styles["template-tag"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#d73a49")) style.styles["template-variable"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#d73a49")) style.styles["type"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#d73a49")) style.styles["variable.language"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#d73a49")) style.styles["title"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#6f42c1")) style.styles["class"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#6f42c1")) style.styles["function"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#6f42c1")) style.styles["attr"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#005cc5")) style.styles["attribute"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#005cc5")) style.styles["literal"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#005cc5")) style.styles["meta"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#005cc5")) style.styles["number"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#005cc5")) style.styles["operator"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#005cc5")) style.styles["variable"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#005cc5")) style.styles["selector-attr"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#005cc5")) style.styles["selector-class"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#005cc5")) style.styles["selector-id"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#005cc5")) style.styles["section"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#005cc5"), traits: [.bold]) style.styles["string"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#032f62")) style.styles["regexp"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#032f62")) style.styles["built_in"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#e36209")) style.styles["symbol"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#e36209")) style.styles["comment"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#6a737d")) style.styles["code"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#6a737d")) style.styles["formula"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#6a737d")) style.styles["name"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#22863a")) style.styles["quote"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#22863a")) style.styles["selector-tag"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#22863a")) style.styles["selector-pseudo"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#22863a")) style.styles["tag"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#22863a")) style.styles["subst"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#24292e")) style.styles["bullet"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#735c0f")) style.styles["emphasis"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#24292e"), traits: [.italic]) style.styles["strong"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#24292e"), traits: [.bold]) style.styles["addition"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#22863a")) style.styles["deletion"] = HighlightStyle.TextStyle(color: PlatformColor(hex: "#b31d28")) return style } } ================================================ FILE: FSNotesCore/SwiftHighlighter/Themes/SolarizedDark.swift ================================================ // // SolarizedDark.swift // FSNotes // // Created by Oleksandr Hlushchenko on 01.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct SolarizedDarkTheme { static func make() -> HighlightStyle { var style = HighlightStyle() style.font = UserDefaultsManagement.codeFont // .hljs style.foregroundColor = PlatformColor(hex: "#839496") style.backgroundColor = PlatformColor(hex: "#002b36") // .hljs-comment, .hljs-quote style.styles["comment"] = .init(color: PlatformColor(hex: "#586e75")) style.styles["quote"] = .init(color: PlatformColor(hex: "#586e75")) // .hljs-keyword, .hljs-selector-tag, .hljs-addition style.styles["keyword"] = .init(color: PlatformColor(hex: "#859900")) style.styles["tag"] = .init(color: PlatformColor(hex: "#859900")) style.styles["addition"] = .init(color: PlatformColor(hex: "#859900")) // .hljs-number, .hljs-string, .hljs-literal, .hljs-regexp, doctag style.styles["number"] = .init(color: PlatformColor(hex: "#2aa198")) style.styles["string"] = .init(color: PlatformColor(hex: "#2aa198")) style.styles["literal"] = .init(color: PlatformColor(hex: "#2aa198")) style.styles["regexp"] = .init(color: PlatformColor(hex: "#2aa198")) // .hljs-title, .hljs-section, .hljs-name, selectors style.styles["function"] = .init(color: PlatformColor(hex: "#268bd2")) style.styles["section"] = .init(color: PlatformColor(hex: "#268bd2")) style.styles["name"] = .init(color: PlatformColor(hex: "#268bd2")) style.styles["class"] = .init(color: PlatformColor(hex: "#268bd2")) // .hljs-attribute, .hljs-variable, .hljs-type style.styles["attribute"] = .init(color: PlatformColor(hex: "#b58900")) style.styles["variable"] = .init(color: PlatformColor(hex: "#b58900")) style.styles["type"] = .init(color: PlatformColor(hex: "#b58900")) // .hljs-symbol, .hljs-bullet, .hljs-subst, .hljs-meta, .hljs-link style.styles["symbol"] = .init(color: PlatformColor(hex: "#cb4b16")) style.styles["bullet"] = .init(color: PlatformColor(hex: "#cb4b16")) style.styles["subst"] = .init(color: PlatformColor(hex: "#cb4b16")) style.styles["meta"] = .init(color: PlatformColor(hex: "#cb4b16")) style.styles["link"] = .init(color: PlatformColor(hex: "#cb4b16")) // .hljs-built_in, .hljs-deletion style.styles["built_in"] = .init(color: PlatformColor(hex: "#dc322f")) style.styles["deletion"] = .init(color: PlatformColor(hex: "#dc322f")) // // .hljs-formula // style.styles["formula"] = .init( // //backgroundColor: PlatformColor(hex: "#073642") // ) // font styles style.styles["emphasis"] = .init( color: PlatformColor(hex: "#cb4b16"), traits: [.italic] ) style.styles["strong"] = .init( color: PlatformColor(hex: "#cb4b16"), traits: [.bold] ) return style } } ================================================ FILE: FSNotesCore/SwiftHighlighter/Themes/SolarizedLight.swift ================================================ // // SolarizedLight.swift // FSNotes // // Created by Oleksandr Hlushchenko on 01.12.2025. // Copyright © 2025 Oleksandr Hlushchenko. All rights reserved. // struct SolarizedLightTheme { static func make() -> HighlightStyle { var style = HighlightStyle() style.font = UserDefaultsManagement.codeFont // .hljs style.foregroundColor = PlatformColor(hex: "#657b83") style.backgroundColor = PlatformColor(hex: "#fdf6e3") // .hljs-comment, .hljs-quote style.styles["comment"] = .init(color: PlatformColor(hex: "#93a1a1")) style.styles["quote"] = .init(color: PlatformColor(hex: "#93a1a1")) // .hljs-keyword, .hljs-selector-tag, .hljs-addition style.styles["keyword"] = .init(color: PlatformColor(hex: "#859900")) style.styles["tag"] = .init(color: PlatformColor(hex: "#859900")) style.styles["addition"] = .init(color: PlatformColor(hex: "#859900")) // .hljs-number, .hljs-string, .hljs-literal, .hljs-regexp style.styles["number"] = .init(color: PlatformColor(hex: "#2aa198")) style.styles["string"] = .init(color: PlatformColor(hex: "#2aa198")) style.styles["literal"] = .init(color: PlatformColor(hex: "#2aa198")) style.styles["regexp"] = .init(color: PlatformColor(hex: "#2aa198")) // .hljs-title, .hljs-section, .hljs-name style.styles["function"] = .init(color: PlatformColor(hex: "#268bd2")) style.styles["section"] = .init(color: PlatformColor(hex: "#268bd2")) style.styles["name"] = .init(color: PlatformColor(hex: "#268bd2")) style.styles["class"] = .init(color: PlatformColor(hex: "#268bd2")) // .hljs-attribute, .hljs-variable, .hljs-type style.styles["attribute"] = .init(color: PlatformColor(hex: "#b58900")) style.styles["variable"] = .init(color: PlatformColor(hex: "#b58900")) style.styles["type"] = .init(color: PlatformColor(hex: "#b58900")) // .hljs-symbol, .hljs-bullet, .hljs-subst, .hljs-meta, .hljs-link style.styles["symbol"] = .init(color: PlatformColor(hex: "#cb4b16")) style.styles["bullet"] = .init(color: PlatformColor(hex: "#cb4b16")) style.styles["subst"] = .init(color: PlatformColor(hex: "#cb4b16")) style.styles["meta"] = .init(color: PlatformColor(hex: "#cb4b16")) style.styles["link"] = .init(color: PlatformColor(hex: "#cb4b16")) // .hljs-built_in, .hljs-deletion style.styles["built_in"] = .init(color: PlatformColor(hex: "#dc322f")) style.styles["deletion"] = .init(color: PlatformColor(hex: "#dc322f")) // font styles style.styles["emphasis"] = .init( color: PlatformColor(hex: "#cb4b16"), traits: [.italic] ) style.styles["strong"] = .init( color: PlatformColor(hex: "#cb4b16"), traits: [.bold] ) return style } } ================================================ FILE: FSNotesCore/TextFormatter.swift ================================================ // // TextFormatter.swift // FSNotes // // Created by Oleksandr Glushchenko on 3/6/18. // Copyright © 2018 Oleksandr Glushchenko. All rights reserved. // import Foundation #if os(OSX) import Cocoa import Carbon.HIToolbox typealias Font = NSFont typealias TextView = EditTextView typealias Color = NSColor #else import UIKit typealias Font = UIFont typealias TextView = EditTextView typealias Color = UIColor #endif public class TextFormatter { private var attributedString: NSMutableAttributedString private var attributedSelected: NSAttributedString private var type: NoteType private var textView: TextView private var note: Note private var storage: NSTextStorage private var selectedRange: NSRange private var range: NSRange private var newSelectedRange: NSRange? private var cursor: Int? private var prevSelectedString: NSAttributedString private var prevSelectedRange: NSRange private var isAutomaticQuoteSubstitutionEnabled: Bool = false private var isAutomaticDashSubstitutionEnabled: Bool = false init(textView: TextView, note: Note) { range = textView.selectedRange #if os(OSX) storage = textView.textStorage! attributedSelected = textView.attributedString() if textView.typingAttributes[.font] == nil { textView.typingAttributes[.font] = UserDefaultsManagement.noteFont } #else storage = textView.textStorage attributedSelected = textView.attributedText #endif self.attributedString = NSMutableAttributedString(attributedString: attributedSelected.attributedSubstring(from: range)) self.selectedRange = NSRange(0.. NSMutableAttributedString { return attributedString } func bold() { if note.isMarkdown() { // UnBold if not selected if range.length == 0 { var resultFound = false let string = getAttributedString().string NotesTextProcessor.boldRegex.matches(string, range: NSRange(0.. Void in guard let range = result?.range else { return } if range.intersection(self.range) != nil { let boldAttributed = self.getAttributedString().attributedSubstring(from: range) self.unBold(attributedString: boldAttributed, range: range) resultFound = true } } if resultFound { return } } // UnBold selected if attributedString.string.contains("**") || attributedString.string.contains("__") { unBold(attributedString: attributedString, range: range) return } var selectRange = NSMakeRange(range.location + 2, 0) let string = attributedString.string let length = string.count if length != 0 { selectRange = NSMakeRange(range.location, length + 4) } let char = UserDefaultsManagement.bold insertText(char + string + char, selectRange: selectRange) } } func italic() { if note.isMarkdown() { // UnItalic if not selected if range.length == 0 { var resultFound = false let string = getAttributedString().string NotesTextProcessor.italicRegex.matches(string, range: NSRange(0.. Void in guard let range = result?.range else { return } if range.intersection(self.range) != nil { let italicAttributed = self.getAttributedString().attributedSubstring(from: range) self.unItalic(attributedString: italicAttributed, range: range) resultFound = true } } if resultFound { return } } // UnItalic if attributedString.string.contains("*") || attributedString.string.contains("_") { unItalic(attributedString: attributedString, range: range) return } var selectRange = NSMakeRange(range.location + 1, 0) let string = attributedString.string let length = string.count if length != 0 { selectRange = NSMakeRange(range.location, length + 2) } let char = UserDefaultsManagement.italic insertText(char + string + char, selectRange: selectRange) } } private func unBold(attributedString: NSAttributedString, range: NSRange) { let unBold = attributedString .string .replacingOccurrences(of: "**", with: "") .replacingOccurrences(of: "__", with: "") let selectRange = NSRange(location: range.location, length: unBold.count) insertText(unBold, replacementRange: range, selectRange: selectRange) } private func unItalic(attributedString: NSAttributedString, range: NSRange) { let unItalic = attributedString .string .replacingOccurrences(of: "*", with: "") .replacingOccurrences(of: "_", with: "") let selectRange = NSRange(location: range.location, length: unItalic.count) insertText(unItalic, replacementRange: range, selectRange: selectRange) } private func unStrike(attributedString: NSAttributedString, range: NSRange) { let unStrike = attributedString .string .replacingOccurrences(of: "~~", with: "") let selectRange = NSRange(location: range.location, length: unStrike.count) insertText(unStrike, replacementRange: range, selectRange: selectRange) } public func underline() { } public func strike() { // UnStrike if not selected if range.length == 0 { var resultFound = false let string = getAttributedString().string NotesTextProcessor.strikeRegex.matches(string, range: NSRange(0.. Void in guard let range = result?.range else { return } if range.intersection(self.range) != nil { let italicAttributed = self.getAttributedString().attributedSubstring(from: range) self.unStrike(attributedString: italicAttributed, range: range) resultFound = true } } if resultFound { return } } // UnStrike if attributedString.string.contains("~~") { unStrike(attributedString: attributedString, range: range) return } var selectRange = NSMakeRange(range.location + 2, 0) let string = attributedString.string let length = string.count if length != 0 { selectRange = NSMakeRange(range.location, length + 4) } insertText("~~" + string + "~~", selectRange: selectRange) } public func tab() { guard let pRange = getParagraphRange() else { return } var padding = "\t" if UserDefaultsManagement.indentUsing == 0x01 { padding = " " } if UserDefaultsManagement.indentUsing == 0x02 { padding = " " } let mutable = NSMutableAttributedString(attributedString: getAttributedString().attributedSubstring(from: pRange)).unloadTasks() let string = mutable.string var result = String() var addsChars = 0 let location = textView.selectedRange.location let length = textView.selectedRange.length var isFirstLine = true string.enumerateLines { (line, _) in result.append(padding + line + "\n") if isFirstLine { isFirstLine = false } else { addsChars += padding.count } } let selectRange = NSRange(location: location + padding.count, length: length + addsChars) let mutableResult = NSMutableAttributedString(string: result) mutableResult.loadTasks() #if os(OSX) textView.textStorage?.removeAttribute(.todo, range: pRange) #else textView.textStorage.removeAttribute(.todo, range: pRange) // Fixes font size issue #1271 let parFont = NotesTextProcessor.font let parRange = NSRange(location: 0, length: mutableResult.length) mutableResult.addAttribute(.font, value: parFont, range: parRange) mutableResult.fixAttributes(in: parRange) #endif insertText(mutableResult, replacementRange: pRange, selectRange: selectRange) } public func unTab() { guard let pRange = getParagraphRange() else { return } let mutable = NSMutableAttributedString(attributedString: storage.attributedSubstring(from: pRange)).unloadTasks() let string = mutable.string var result = String() let location = textView.selectedRange.location let length = textView.selectedRange.length var padding = 0 var dropChars = 0 if string.starts(with: "\t") { padding = 1 } else if string.starts(with: " ") && UserDefaultsManagement.indentUsing == 0x01 { padding = 2 } else if string.starts(with: " ") { padding = 4 } if padding == 0 { return } var isFirstLine = true string.enumerateLines { (line, _) in var line = line if !line.isEmpty { var firstCharsToDrop: Int? if line.first == "\t" { firstCharsToDrop = 1 } else if UserDefaultsManagement.indentUsing == 0x01 && line.starts(with: " ") { firstCharsToDrop = 2 } else if line.starts(with: " ") { firstCharsToDrop = 4 } if let x = firstCharsToDrop { line = String(line.dropFirst(x)) if length == 0 { dropChars = 0 } else { if isFirstLine { isFirstLine = false } else { dropChars += x } } } } result.append(line + "\n") } let diffLocation = location - padding var selectLength = length - dropChars var selectLocation = diffLocation > 0 ? diffLocation : 0 if selectLocation < pRange.location { selectLocation = pRange.location } if selectLength > result.count { selectLength = result.count } let selectRange = NSRange(location: selectLocation, length: selectLength) let mutableResult = NSMutableAttributedString(string: result) mutableResult.loadTasks() #if os(OSX) textView.textStorage?.removeAttribute(.todo, range: pRange) #else textView.textStorage.removeAttribute(.todo, range: pRange) // Fixes font size issue #1271 let parFont = NotesTextProcessor.font let parRange = NSRange(location: 0, length: mutableResult.length) mutableResult.addAttribute(.font, value: parFont, range: parRange) mutableResult.fixAttributes(in: parRange) #endif insertText(mutableResult, replacementRange: pRange, selectRange: selectRange) } public func header(_ string: String) { let fullSelection = selectedRange.length > 0 guard let pRange = getParagraphRange() else { return } #if os(iOS) var prefix = String() var paragraph = storage.mutableString.substring(with: pRange) if paragraph.starts(with: "######") { paragraph = paragraph .replacingOccurrences(of: "#", with: "") .trimSpaces() } else if paragraph.starts(with: "#") { prefix = string } else { prefix = string + " " } let diff = paragraph.contains("\n") ? 1 : 0 let selectRange = NSRange(location: pRange.location + (prefix + paragraph).count - diff, length: 0) insertText(prefix + paragraph, replacementRange: pRange, selectRange: selectRange) #else let prefix = string + " " var paragraph = storage.mutableString .substring(with: pRange) if paragraph.starts(with: prefix) { paragraph = paragraph.replacingOccurrences(of: prefix, with: "") } else { paragraph = prefix + paragraph.replacingOccurrences(of: "#", with: "").trimSpaces() } let diff = paragraph.contains("\n") ? 1 : 0 var selectRange = NSRange(location: pRange.location + paragraph.count - diff, length: 0) if fullSelection { selectRange = NSRange(location: pRange.location, length: paragraph.count - diff) } insertText(paragraph, replacementRange: pRange, selectRange: selectRange) #endif } public func link() { textView.undoManager?.beginUndoGrouping() let text = "[" + attributedString.string + "]()" replaceWith(string: text, range: range) if (attributedString.length == 4) { setSelectedRange(NSMakeRange(range.location + 1, 0)) } else { setSelectedRange(NSMakeRange(range.upperBound + 3, 0)) } textView.undoManager?.endUndoGrouping() } public func wikiLink() { textView.undoManager?.beginUndoGrouping() let text = "[[" + attributedString.string + "]]" replaceWith(string: text, range: range) if (text.count == 4) { setSelectedRange(NSMakeRange(range.location + 2, 0)) #if os(OSX) textView.complete(nil) #endif } else { setSelectedRange(NSMakeRange(range.location + 2, text.count - 4)) } textView.undoManager?.endUndoGrouping() } public func image() { textView.undoManager?.beginUndoGrouping() let text = "![" + attributedString.string + "]()" replaceWith(string: text) if (attributedString.length == 5) { setSelectedRange(NSMakeRange(range.location + 2, 0)) } else { setSelectedRange(NSMakeRange(range.upperBound + 4, 0)) } textView.undoManager?.endUndoGrouping() } public func isListParagraph() -> Bool { guard let currentPR = getParagraphRange() else { return false } let paragraph = storage.attributedSubstring(from: currentPR) if TextFormatter.getAutocompleteCharsMatch(string: paragraph.string) != nil { return true } if TextFormatter.getAutocompleteDigitsMatch(string: paragraph.string) != nil { return true } if paragraph.hasTodoAttribute() { return true } return false } public func tabKey() { guard let currentPR = getParagraphRange() else { return } let paragraph = storage.attributedSubstring(from: currentPR).string let sRange = self.textView.selectedRange // Middle if (sRange.location != 0 || sRange.location != storage.length) && paragraph.count == 1 && self.note.isMarkdown() { self.insertText("\t", replacementRange: sRange) return } // First & Last if (sRange.location == 0 || sRange.location == self.storage.length) && paragraph.count == 0 && self.note.isMarkdown() { self.insertText("\t\n", replacementRange: sRange) self.setSelectedRange(NSRange(location: sRange.location + 1, length: 0)) return } self.insertText("\t") } public static func getAutocompleteCharsMatch(string: String) -> NSTextCheckingResult? { guard let regex = try? NSRegularExpression(pattern: "^(( |\t)*\\- \\[[x| ]*\\] )|^(( |\t)*[-|–|—|*|•|>|\\+]{1} )"), let result = regex.firstMatch(in: string, range: NSRange(0.. NSTextCheckingResult? { guard let regex = try? NSRegularExpression(pattern: "^(( |\t)*[0-9]+\\. )"), let result = regex.firstMatch(in: string, range: NSRange(0..= match.range.upperBound else { return } let found = string.attributedSubstring(from: match.range).string var newLine = 1 if textView.selectedRange.upperBound == storage.length { newLine = 0 } if found.count + newLine == string.length { let range = storage.mutableString.paragraphRange(for: textView.selectedRange) let selectRange = NSRange(location: range.location, length: 0) insertText("\n", replacementRange: range, selectRange: selectRange) } else { insertText("\n" + found) } updateCurrentParagraph() } private func matchDigits(string: NSAttributedString, match: NSTextCheckingResult) { guard string.length >= match.range.upperBound else { return } let found = string.attributedSubstring(from: match.range).string var newLine = 1 if textView.selectedRange.upperBound == storage.length { newLine = 0 } if found.count + newLine == string.length { let range = storage.mutableString.paragraphRange(for: textView.selectedRange) let selectRange = NSRange(location: range.location, length: 0) insertText("\n", replacementRange: range, selectRange: selectRange) } else if let position = Int(found.replacingOccurrences(of:"[^0-9]", with: "", options: .regularExpression)) { let newDigit = found.replacingOccurrences(of: String(position), with: String(position + 1)) insertText("\n" + newDigit) } updateCurrentParagraph() } private func updateCurrentParagraph() { let parRange = getParagraphRange(for: textView.selectedRange.location) #if os(iOS) textView.textStorage.updateParagraphStyle(range: parRange) #else textView.textStorage?.updateParagraphStyle(range: parRange) #endif } public func newLine() { guard let currentParagraphRange = self.getParagraphRange() else { return } let currentParagraph = storage.attributedSubstring(from: currentParagraphRange) let selectedRange = self.textView.selectedRange // Autocomplete todo lists if selectedRange.location != currentParagraphRange.location && currentParagraphRange.upperBound - 2 < selectedRange.location, currentParagraph.length >= 2 { if textView.selectedRange.upperBound > 2 { let char = storage.attributedSubstring(from: NSRange(location: textView.selectedRange.upperBound - 2, length: 1)) if let _ = char.attribute(.todo, at: 0, effectiveRange: nil) { let selectRange = NSRange(location: currentParagraphRange.location, length: 0) insertText("", replacementRange: currentParagraphRange, selectRange: selectRange) #if os(OSX) textView.insertNewline(nil) textView.setSelectedRange(selectRange) #else textView.insertText("\n") textView.selectedRange = selectRange #endif return } } var todoLocation = -1 currentParagraph.enumerateAttribute(.todo, in: NSRange(0.. Void in guard value != nil else { return } todoLocation = range.location stop.pointee = true } if todoLocation > -1 { let unchecked = AttributedBox.getUnChecked()?.attributedSubstring(from: NSRange(0..<2)) var prefix = String() if todoLocation > 0 { prefix = currentParagraph.attributedSubstring(from: NSRange(0.. 0 { var j = 0 for char in line { j += 1 if (char.isWhitespace || char == "\t") && !scanFinished { if j == line.count { empty.append("\(char)" + task) } else { empty.append(char) } } else { if !scanFinished { empty.append(task + "\(char)") scanFinished = true } else { empty.append(char) } } } result += empty + "\n" } else { result += task + "\n" } } else { result += line + "\n" } } let mutableResult = NSMutableAttributedString(string: result) #if os(iOS) let textColor: UIColor = UIColor.blackWhite #else let textColor: NSColor = NotesTextProcessor.fontColor #endif mutableResult.addAttribute(.foregroundColor, value: textColor, range: NSRange(location: 0, length: mutableResult.length)) mutableResult.addAttribute(.font, value: NotesTextProcessor.font, range: NSRange(location: 0, length: mutableResult.length)) mutableResult.fixAttributes(in: NSRange(location: 0, length: mutableResult.length)) mutableResult.loadTasks() let diff = mutableResult.length - attributedString.length let selectRange = selectedRange.length == 0 || lines.count == 1 ? NSRange(location: pRange.location + pRange.length + diff - 1, length: 0) : NSRange(location: pRange.location, length: mutableResult.length) // Fixes clicked area storage.removeAttribute(.todo, range: pRange) insertText(mutableResult, replacementRange: pRange, selectRange: selectRange) } public func toggleTodo(_ location: Int? = nil) { if let location = location, let todoAttr = storage.attribute(.todo, at: location, effectiveRange: nil) as? Int { #if os(OSX) if textView.window?.firstResponder != textView { textView.window?.makeFirstResponder(textView) } #endif guard let paragraph = getParagraphRange(for: location) else { return } let paragraphTextNonMutable = storage.attributedSubstring(from: paragraph) let paragraphText = NSMutableAttributedString(attributedString: paragraphTextNonMutable) let attributedText = (todoAttr == 0) ? AttributedBox.getChecked(clean: true) : AttributedBox.getUnChecked(clean: true) let checkboxLocation = location - paragraph.location paragraphText.replaceCharacters(in: NSRange(location: checkboxLocation, length: 1), with: attributedText!) if todoAttr == 0 { paragraphText.addAttribute(.strikethroughStyle, value: 1, range: NSRange(location: 0, length: paragraphText.length)) textView.typingAttributes[.strikethroughStyle] = 1 } else { paragraphText.removeAttribute(.strikethroughStyle, range: NSRange(location: 0, length: paragraphText.length)) textView.typingAttributes.removeValue(forKey: .strikethroughStyle) } insertText(paragraphText, replacementRange: paragraph) if todoAttr == 1 { storage.removeAttribute(.strikethroughStyle, range: paragraph) } updateCurrentParagraph() return } guard let paragraphRange = getParagraphRange() else { return } let paragraph = self.storage.attributedSubstring(from: paragraphRange) if let index = paragraph.string.range(of: "- [ ] ") { let local = paragraph.string.nsRange(from: index).location let range = NSMakeRange(paragraphRange.location + local, 6) if let attributedText = AttributedBox.getChecked() { self.insertText(attributedText, replacementRange: range) } return } else if let index = paragraph.string.range(of: "- [x] ") { let local = paragraph.string.nsRange(from: index).location let range = NSMakeRange(paragraphRange.location + local, 6) if let attributedText = AttributedBox.getUnChecked() { self.insertText(attributedText, replacementRange: range) } return } } public func backTick() { let selectedRange = textView.selectedRange if selectedRange.length > 0 { let text = storage.attributedSubstring(from: selectedRange).string let string = "`\(text)`" let codeFont = UserDefaultsManagement.codeFont let mutableString = NSMutableAttributedString(string: string) mutableString.addAttribute(.font, value: codeFont, range: NSRange(0.. 0 { let substring = storage.attributedSubstring(from: currentRange) let mutable = NSMutableAttributedString(string: "```\n") mutable.append(substring) if substring.string.last != "\n" { mutable.append(NSAttributedString(string: "\n")) } mutable.append(NSAttributedString(string: "```\n")) insertText(mutable.string, replacementRange: currentRange) setSelectedRange(NSRange(location: currentRange.location + 3, length: 0)) return } insertText("```\n\n```\n") setSelectedRange(NSRange(location: currentRange.location + 4, length: 0)) } public func quote() { guard let pRange = getParagraphRange() else { return } let paragraph = storage.mutableString.substring(with: pRange) guard paragraph.isContainsLetters else { insertText("> ") return } var hasPrefix = false var lines = [String]() paragraph.enumerateLines { (line, _) in hasPrefix = line.starts(with: "> ") var skipNext = false var scanFinished = false var cleanLine = String() for char in line { if skipNext { skipNext = false continue } if char == ">" && !scanFinished { skipNext = true scanFinished = true } else { cleanLine.append(char) } } lines.append(cleanLine) } var result = String() for line in lines { if hasPrefix { result += line + "\n" } else { result += "> " + line + "\n" } } let selectRange = selectedRange.length == 0 || lines.count == 1 ? NSRange(location: pRange.location + result.count - 1, length: 0) : NSRange(location: pRange.location, length: result.count) insertText(result, replacementRange: pRange, selectRange: selectRange) } private func getAttributedTodoString(_ string: String) -> NSAttributedString { let string = NSMutableAttributedString(string: string) string.addAttribute(.foregroundColor, value: NotesTextProcessor.syntaxColor, range: NSRange(0..<1)) var color = Color.black #if os(OSX) color = NSColor(named: "mainText")! #endif string.addAttribute(.foregroundColor, value: color, range: NSRange(1.. NSRange? { if range.upperBound <= storage.length { let paragraph = storage.mutableString.paragraphRange(for: range) return paragraph } return nil } private func getParagraphRange(for location: Int) -> NSRange? { guard location <= storage.length else { return nil} let range = NSRange(location: location, length: 0) let paragraphRange = storage.mutableString.paragraphRange(for: range) return paragraphRange } func getTypingAttributes() -> Font { #if os(OSX) return textView.typingAttributes[.font] as! Font #else if let typingFont = textView.typingFont { textView.typingFont = nil return typingFont } guard textView.textStorage.length > 0, textView.selectedRange.location > 0 else { return UserDefaultsManagement.noteFont } let i = textView.selectedRange.location - 1 let upper = textView.selectedRange.upperBound let substring = textView.attributedText.attributedSubstring(from: NSRange(i.. NSColor { var color = NSColor(named: "mainText")! return color } #endif func setTypingAttributes(font: Font) { #if os(OSX) textView.typingAttributes[.font] = font #else textView.typingFont = font textView.typingAttributes[.font] = font #endif } public func setSelectedRange(_ range: NSRange) { #if os(OSX) if range.upperBound <= storage.length { textView.setSelectedRange(range) } #else textView.selectedRange = range #endif } func getAttributedString() -> NSAttributedString { #if os(OSX) return textView.attributedString() #else return textView.attributedText #endif } private func insertText(_ string: Any, replacementRange: NSRange? = nil, selectRange: NSRange? = nil) { let range = replacementRange ?? self.textView.selectedRange #if os(iOS) guard let start = textView.position(from: self.textView.beginningOfDocument, offset: range.location), let end = textView.position(from: start, offset: range.length), let selectedRange = textView.textRange(from: start, to: end) else { return } var replaceString = String() if let attributedString = string as? NSAttributedString { replaceString = attributedString.string } if let plainString = string as? String { replaceString = plainString } self.textView.undoManager?.beginUndoGrouping() self.textView.replace(selectedRange, withText: replaceString) if let string = string as? NSAttributedString { let editedRange = NSRange(location: range.location, length: replaceString.count) storage.replaceCharacters(in: editedRange, with: string) storage.delegate?.textStorage!(storage, didProcessEditing: NSTextStorage.EditActions.editedCharacters, range: editedRange, changeInLength: 1) } else { let parRange = NSRange(location: range.location, length: replaceString.count) let parStyle = NSMutableParagraphStyle() parStyle.alignment = .left parStyle.lineSpacing = CGFloat(UserDefaultsManagement.editorLineSpacing) self.textView.textStorage.addAttribute(.paragraphStyle, value: parStyle, range: parRange) } self.textView.undoManager?.endUndoGrouping() #else textView.insertText(string, replacementRange: range) #endif if let select = selectRange { setSelectedRange(select) } } public static func getAttributedCode(string: String) -> NSMutableAttributedString { let attributedString = NSMutableAttributedString(string: string) let range = NSRange(0.. String { var line = line let digitRegex = try! NSRegularExpression(pattern: "^([0-9]+\\. )") let digitRegexResult = digitRegex.firstMatch(in: line, range: NSRange(0.. (Bool, Bool, String) { var count = 0 var hasTodoPrefix = false var hasIncompletedTask = false var charFound = false var whitespacePrefix = String() var letterPrefix = String() for char in line { if char.isWhitespace && !charFound { count += 1 whitespacePrefix.append(char) continue } else { charFound = true letterPrefix.append(char) } } if letterPrefix.starts(with: "- [ ] ") { hasTodoPrefix = false hasIncompletedTask = true } if letterPrefix.starts(with: "- [x] ") { hasTodoPrefix = true } letterPrefix = letterPrefix .replacingOccurrences(of: "- [ ] ", with: "") .replacingOccurrences(of: "- [x] ", with: "") return (hasTodoPrefix, hasIncompletedTask, whitespacePrefix + letterPrefix) } private func hasPrefix(line: String, numbers: Bool) -> Bool { if line.starts(with: "- [ ] ") || line.starts(with: "- [x] ") { return false } var checkNumberDot = false for char in line { if checkNumberDot { if char == "." { return numbers } } if char.isWhitespace { continue } else { if char.isNumber { checkNumberDot = true continue } else if char == "-" { return !numbers } else { return false } } } return false } } ================================================ FILE: FSNotesCore/TextStorageProcessor.swift ================================================ // // TextStorageProcessor.swift // FSNotes // // Created by Oleksandr Hlushchenko on 26.06.2022. // Copyright © 2022 Oleksandr Hlushchenko. All rights reserved. // #if os(OSX) import Cocoa import AVKit #else import UIKit import AVKit #endif class TextStorageProcessor: NSObject, NSTextStorageDelegate { public var editor: EditTextView? public var detector = CodeBlockDetector() #if os(iOS) public func textStorage( _ textStorage: NSTextStorage, didProcessEditing editedMask: NSTextStorage.EditActions, range editedRange: NSRange, changeInLength delta: Int) { guard editedMask != .editedAttributes else { return } process(textStorage: textStorage, range: editedRange, changeInLength: delta) } #else public func textStorage( _ textStorage: NSTextStorage, didProcessEditing editedMask: NSTextStorageEditActions, range editedRange: NSRange, changeInLength delta: Int) { guard editedMask != .editedAttributes else { return } process(textStorage: textStorage, range: editedRange, changeInLength: delta) if editedMask.contains(.editedCharacters), delta < 0 { if let layoutManager = textStorage.layoutManagers.first, let textContainer = layoutManager.textContainers.first, let textView = textContainer.textView { textView.needsDisplay = true } } } #endif private func process(textStorage: NSTextStorage, range editedRange: NSRange, changeInLength delta: Int) { guard let note = editor?.note, textStorage.length > 0 else { return } defer { loadImages(textStorage: textStorage, checkRange: editedRange) textStorage.updateParagraphStyle(range: editedRange) } if note.content.length == textStorage.length && ( note.content.string.fnv1a == note.cacheHash ) { return } // Full load if editedRange.length == textStorage.length { NotesTextProcessor.highlight(attributedString: textStorage) return } let codeBlockRanges = detector.findCodeBlocks(in: textStorage) let paragraphRange = (textStorage.string as NSString).paragraphRange(for: editedRange) NotesTextProcessor.highlightMarkdown(attributedString: textStorage, paragraphRange: paragraphRange, codeBlockRanges: codeBlockRanges) // Code block founds var result = detector.codeBlocks(textStorage: textStorage, editedRange: editedRange, delta: delta, newRanges: codeBlockRanges) note.codeBlockRangesCache = codeBlockRanges // Highlight code block end (```), that wiped previously in highlightMarkdown for range in codeBlockRanges { if NSIntersectionRange(range, paragraphRange).length > 0 { if result.edited == nil { result.code?.append(range) } } } if let ranges = result.code { for range in ranges { NotesTextProcessor .getHighlighter() .highlight(in: textStorage, fullRange: range) } } if let editedBlock = result.edited, let editedParagraph = result.editedParagraph { NotesTextProcessor .getHighlighter() .highlight(in: textStorage, fullRange: editedBlock, editedRange: editedParagraph) } if let ranges = result.md { for range in ranges { let safeRange = safeRange(range, in: textStorage) NotesTextProcessor.resetFont(attributedString: textStorage, paragraphRange: safeRange) NotesTextProcessor.highlightMarkdown(attributedString: textStorage, paragraphRange: safeRange) } } } private func loadImages(textStorage: NSTextStorage, checkRange: NSRange) { guard let note = editor?.note else { return } var start = checkRange.lowerBound var finish = checkRange.upperBound if checkRange.upperBound < textStorage.length { finish = checkRange.upperBound + 1 } if checkRange.lowerBound > 1 { start = checkRange.lowerBound - 1 } let affectedRange = NSRange(start.. CGFloat { #if os(iOS) return UIApplication.getVC().view.frame.width - 35 #else return CGFloat(UserDefaultsManagement.imagesWidth) #endif } private func safeRange(_ range: NSRange, in textStorage: NSTextStorage) -> NSRange { let storageLength = textStorage.length let loc = min(max(0, range.location), storageLength) let end = min(max(0, range.location + range.length), storageLength) return NSRange(location: loc, length: end - loc) } } ================================================ FILE: FSNotesCore/UserDefaultsManagement.swift ================================================ // // Preferences.swift // FSNotes // // Created by Oleksandr Glushchenko on 8/8/17. // Copyright © 2017 Oleksandr Glushchenko. All rights reserved. // import Foundation #if os(OSX) import Cocoa #else import UIKit #endif public class UserDefaultsManagement { static var apiPath = "https://api.fsnot.es/" static var webPath = "https://p.fsnot.es/" public static var global = NSUbiquitousKeyValueStore.default #if os(OSX) typealias Color = NSColor typealias Image = NSImage typealias Font = NSFont public static var shared: UserDefaults? = UserDefaults.standard public static var DefaultFontSize = 14 #else typealias Color = UIColor typealias Image = UIImage typealias Font = UIFont public static var shared: UserDefaults? = UserDefaults(suiteName: "group.es.fsnot.user.defaults") static var DefaultFontSize = 17 #endif static var DefaultSnapshotsInterval = 1 static var DefaultSnapshotsIntervalMinutes = 5 static var DefaultFontColor = Color.black static var DefaultBgColor = Color.white private struct Constants { static let AllowTouchID = "allowTouchID" static let AppearanceTypeKey = "appearanceType" static let AskCommitMessage = "askCommitMessage" static let ApiBookmarksData = "apiBookmarksData" static let AutoInsertHeader = "autoInsertHeader" static let AutoVersioning = "autoVersioning" static let AutomaticSpellingCorrection = "automaticSpellingCorrection" static let AutomaticQuoteSubstitution = "automaticQuoteSubstitution" static let AutomaticDataDetection = "automaticDataDetection" static let AutomaticLinkDetection = "automaticLinkDetection" static let AutomaticTextReplacement = "automaticTextReplacement" static let AutomaticDashSubstitution = "automaticDashSubstitution" static let AutomaticConflictsResolution = "automaticConflictsResolution" static let BackupManually = "backupManually" static let BgColorKey = "bgColorKeyed" static let boldKey = "boldKeyed" static let CacheDiff = "cacheDiff" static let CellSpacing = "cellSpacing" static let CellFrameOriginY = "cellFrameOriginY" static let ClickableLinks = "clickableLinks" static let CodeFontNameKey = "codeFont" static let CodeFontSizeKey = "codeFontSize" static let codeBlockHighlight = "codeBlockHighlight" static let CodeBlocksWithSyntaxHighlighting = "codeBlocksWithSyntaxHighlighting" static let codeTheme = "codeTheme2025" static let ContinuousSpellChecking = "continuousSpellChecking" static let CrashedLastTime = "crashedLastTime" static let CustomWebServer = "customWebServer" static let DefaultLanguageKey = "defaultLanguage" static let DefaultKeyboardKey = "defaultKeyboard" static let FontNameKey = "font" static let FontSizeKey = "fontsize" static let FontColorKey = "fontColorKeyed" static let FullScreen = "fullScreen" static let FirstLineAsTitle = "firstLineAsTitle" static let MaxChildDirs = "maxChildDirs" static let NoteType = "noteType" static let NoteExtension = "noteExtension" static let GrammarChecking = "grammarChecking" static let GitStorage = "gitStorage" static let GitUsername = "gitUsername" static let GitPassword = "gitPassword" static let GitOrigin = "gitOrigin" static let GitPrivateKeyData = "gitPrivateKeyData" static let GitPasspharse = "gitPasspharse" static let HideDate = "hideDate" static let HideOnDeactivate = "hideOnDeactivate" static let HideSidebar = "hideSidebar" static let HidePreviewKey = "hidePreview" static let HidePreviewImages = "hidePreviewImages" static let iCloudDrive = "iCloudDrive" static let ImagesWidthKey = "imagesWidthKey" static let IndentUsing = "indentUsing" static let InlineTags = "inlineTags" static let IsFirstLaunch = "isFirstLaunch" static let italicKey = "italicKeyed" static let LastCommitMessage = "lastCommitMessage" static let LastNews = "lastNews" static let LastSelectedPath = "lastSelectedPath" static let LastScreenX = "lastScreenX" static let LastScreenY = "lastScreenY" static let LastSidebarItem = "lastSidebarItem" static let LastProjectURL = "lastProjectUrl" static let LineHeightMultipleKey = "lineHeightMultipleKey" static let LineSpacingEditorKey = "lineSpacingEditor" static let LineWidthKey = "lineWidth" static let LockOnSleep = "lockOnSleep" static let LockOnScreenActivated = "lockOnScreenActivated" static let LockAfterIDLE = "lockAfterIdle" static let LockAfterUserSwitch = "lockAfterUserSwitch" static let MarginSizeKey = "marginSize" static let MasterPasswordHint = "masterPasswordHint" static let MathJaxPreview = "mathJaxPreview" static let NonContiguousLayout = "allowsNonContiguousLayout" static let NoteContainer = "noteContainer" static let Preview = "preview" static let PreviewFontSize = "previewFontSize" static let ProjectsKey = "projects" static let ProjectsKeyNew = "ProjectsKeyNew" static let RecentSearches = "recentSearches" static let PullInterval = "pullInterval" static let SaveInKeychain = "saveInKeychain" static let SearchHighlight = "searchHighlighting" static let SeparateRepo = "separateRepo" static let SftpHost = "sftpHost" static let SftpPort = "sftpPort" static let SftpPath = "sftpPath" static let SftpPasspharse = "sftpPassphrase" static let SftpWeb = "sftpWeb" static let SftpUsername = "sftpUsername" static let SftpPassword = "sftpPassword" static let SftpKeysAccessData = "sftpKeysAccessData" static let SftpUploadBookmarksData = "sftpUploadBookmarksData" static let SharedContainerKey = "sharedContainer" static let ShowDockIcon = "showDockIcon" static let shouldFocusSearchOnESCKeyDown = "shouldFocusSearchOnESCKeyDown" static let ShowInMenuBar = "showInMenuBar" static let SmartInsertDelete = "smartInsertDelete" static let SnapshotsInterval = "snapshotsInterval" static let SnapshotsIntervalMinutes = "snapshotsIntervalMinutes" static let SortBy = "sortBy" static let StorageType = "storageType" static let StoragePathKey = "storageUrl" static let TableOrientation = "isUseHorizontalMode" static let TextMatchAutoSelection = "textMatchAutoSelection" static let TrashKey = "trashKey" static let UploadKey = "uploadKey" static let UseTextBundleToStoreDates = "useTextBundleToStoreDates" static let AutocloseBrackets = "autocloseBrackets" static let Welcome = "welcome2026" } static var codeFontName: String { get { if let returnFontName = shared?.object(forKey: Constants.CodeFontNameKey) as? String { return returnFontName } else { return "Source Code Pro" } } set { shared?.set(newValue, forKey: Constants.CodeFontNameKey) } } static var codeFontSize: Int { get { if let returnFontSize = shared?.object(forKey: Constants.CodeFontSizeKey) as? Int { return returnFontSize } else { return self.DefaultFontSize } } set { shared?.set(newValue, forKey: Constants.CodeFontSizeKey) } } static var fontName: String? { get { if let returnFontName = shared?.object(forKey: Constants.FontNameKey) as? String { return returnFontName } return nil } set { shared?.set(newValue, forKey: Constants.FontNameKey) } } static var fontSize: Int { get { if let returnFontSize = shared?.object(forKey: Constants.FontSizeKey) as? Int { return returnFontSize } else { return self.DefaultFontSize } } set { shared?.set(newValue, forKey: Constants.FontSizeKey) } } static var externalEditor: String { get { if let name = shared?.object(forKey: "externalEditorApp") as? String, name.count > 0 { return name } else { return "TextEdit" } } set { shared?.set(newValue, forKey: "externalEditorApp") } } static var horizontalOrientation: Bool { get { if let returnHorizontalOrientation = shared?.object(forKey: Constants.TableOrientation) as? Bool { return returnHorizontalOrientation } else { return false } } set { shared?.set(newValue, forKey: Constants.TableOrientation) // reset the note list height / width shared?.removeObject(forKey: "NSSplitView Subview Frames EditorSplitView") if (newValue){ // for top-to-bottom layout, set note list cell height to 0 cellSpacing = 0 } else { // for side-by-side layout, reset note list cell height to default shared?.removeObject(forKey: Constants.CellSpacing) } } } static var iCloudDocumentsContainer: URL? { get { if let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents").standardized { if (!FileManager.default.fileExists(atPath: iCloudDocumentsURL.path, isDirectory: nil)) { do { try FileManager.default.createDirectory(at: iCloudDocumentsURL, withIntermediateDirectories: true, attributes: nil) return iCloudDocumentsURL.standardized } catch { print("Home directory creation: \(error)") } } else { return iCloudDocumentsURL.standardized } } return nil } } static var localDocumentsContainer: URL? { get { if var path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first { #if os(iOS) if path.starts(with: "/var") { path = "/private\(path)" } #endif return URL(fileURLWithPath: path, isDirectory: true) } return nil } } static var customStoragePath: String? { get { if let storagePath = shared?.object(forKey: Constants.StoragePathKey) as? String { if FileManager.default.isWritableFile(atPath: storagePath) { storageType = .custom return storagePath } else { print("Storage path not accessible, settings resetted to default") } } return nil } set { shared?.set(newValue, forKey: Constants.StoragePathKey) } } static var storagePath: String? { get { if let customStoragePath = self.customStoragePath { return customStoragePath } if let iCloudDocumentsURL = self.iCloudDocumentsContainer { storageType = .iCloudDrive return iCloudDocumentsURL.path } if let localDocumentsContainer = localDocumentsContainer { storageType = .local return localDocumentsContainer.path } return nil } } public static var storageType: StorageType { get { if let type = shared?.object(forKey: Constants.StorageType) as? Int { return StorageType(rawValue: type) ?? .none } return .none } set { shared?.set(newValue.rawValue, forKey: Constants.StorageType) } } static var storageUrl: URL? { get { if let path = storagePath { let expanded = NSString(string: path).expandingTildeInPath return URL.init(fileURLWithPath: expanded, isDirectory: true).standardized } return nil } } static var preview: Bool { get { if let preview = shared?.object(forKey: Constants.Preview) as? Bool { return preview } else { return false } } set { shared?.set(newValue, forKey: Constants.Preview) } } static var lastSync: Date? { get { if let sync = shared?.object(forKey: "lastSync") as? Date { return sync } else { return nil } } set { shared?.set(newValue, forKey: "lastSync") } } static var hideOnDeactivate: Bool { get { if let hideOnDeactivate = shared?.object(forKey: Constants.HideOnDeactivate) as? Bool { return hideOnDeactivate } else { return false } } set { shared?.set(newValue, forKey: Constants.HideOnDeactivate) } } static var cellSpacing: Int { get { if let cellSpacing = shared?.object(forKey: Constants.CellSpacing) as? NSNumber { return cellSpacing.intValue } else { return 33 } } set { shared?.set(newValue, forKey: Constants.CellSpacing) } } static var cellViewFrameOriginY: CGFloat? { get { if let number = shared?.object(forKey: Constants.CellFrameOriginY) as? NSNumber { return CGFloat(number.doubleValue) } return nil } set { if let newValue = newValue { shared?.set(Double(newValue), forKey: Constants.CellFrameOriginY) } else { shared?.removeObject(forKey: Constants.CellFrameOriginY) } } } static var hidePreview: Bool { get { if let returnMode = shared?.object(forKey: Constants.HidePreviewKey) as? Bool { return returnMode } else { return false } } set { shared?.set(newValue, forKey: Constants.HidePreviewKey) } } static var sort: SortBy { get { if let result = global.object(forKey: "sortBy") as? String, let sortBy = SortBy(rawValue: result) { return sortBy } else { return .modificationDate } } set { global.set(newValue.rawValue, forKey: "sortBy") } } static var sortDirection: Bool { get { if let returnMode = global.object(forKey: "sortDirection") as? Bool { return returnMode } else { return true } } set { global.set(newValue, forKey: "sortDirection") } } static var hideSidebar: Bool { get { if let hide = shared?.object(forKey: "hideSidebar") as? Bool { return hide } return false } set { shared?.set(newValue, forKey: "hideSidebar") } } static var notesTableWidth: CGFloat { get { if let value = shared?.object(forKey: "sidebarSize") as? Int { return CGFloat(value) } #if os(iOS) return 0 #else return 300 #endif } set { shared?.set(Int(newValue), forKey: "sidebarSize") } } static var hideSidebarTable: Bool { get { if let hide = shared?.object(forKey: "hideRealSidebar") as? Bool { return hide } return false } set { shared?.set(newValue, forKey: "hideRealSidebar") } } static var sidebarTableWidth: CGFloat { get { if let size = shared?.object(forKey: "realSidebarSize") as? Int { return CGFloat(size) } return 150 } set { shared?.set(Int(newValue), forKey: "realSidebarSize") } } static var codeBlockHighlight: Bool { get { if let highlight = shared?.object(forKey: Constants.codeBlockHighlight) as? Bool { return highlight } return true } set { shared?.set(newValue, forKey: Constants.codeBlockHighlight) } } static var lastSelectedURL: URL? { get { if let url = shared?.url(forKey: Constants.LastSelectedPath) { return url } return nil } set { shared?.set(newValue, forKey: Constants.LastSelectedPath) } } static var focusInEditorOnNoteSelect: Bool { get { if let result = shared?.object(forKey: "focusInEditorOnNoteSelect") as? Bool { return result } return false } set { shared?.set(newValue, forKey: "focusInEditorOnNoteSelect") } } static var defaultKeyboard: String? { get { if let dk = shared?.string(forKey: Constants.DefaultKeyboardKey) as? String { return dk } return nil } set { shared?.set(newValue, forKey: Constants.DefaultKeyboardKey) } } static var defaultLanguage: Int { get { if let dl = shared?.object(forKey: Constants.DefaultLanguageKey) as? Int { return dl } if let code = NSLocale.current.languageCode { return LanguageType.withCode(rawValue: code) } return 0 } set { shared?.set(newValue, forKey: Constants.DefaultLanguageKey) } } static var autocloseBrackets: Bool { get { if let result = shared?.object(forKey: Constants.AutocloseBrackets) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.AutocloseBrackets) } } static var lastProjectURL: URL? { get { if let lastProject = shared?.url(forKey: Constants.LastProjectURL) { return lastProject } return nil } set { shared?.set(newValue, forKey: Constants.LastProjectURL) } } static var lastSidebarItem: Int? { get { if let index = shared?.object(forKey: Constants.LastSidebarItem) as? Int { return index } return nil } set { shared?.set(newValue, forKey: Constants.LastSidebarItem) } } static var showDockIcon: Bool { get { if let result = shared?.object(forKey: Constants.ShowDockIcon) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.ShowDockIcon) } } static var editorLineSpacing: Float { get { if let result = shared?.object(forKey: Constants.LineSpacingEditorKey) as? Float { return Float(Int(result)) } else { #if os(iOS) return 6 #else return 4 #endif } } set { shared?.set(newValue, forKey: Constants.LineSpacingEditorKey) } } static var lineHeightMultiple: CGFloat { get { if let result = shared?.object(forKey: Constants.LineHeightMultipleKey) as? Float { return CGFloat(result) } else { return 1.4 } } set { shared?.set(Float(newValue), forKey: Constants.LineHeightMultipleKey) } } static var imagesWidth: Float { get { if let result = shared?.object(forKey: Constants.ImagesWidthKey) as? Float { return result } return 450 } set { shared?.set(newValue, forKey: Constants.ImagesWidthKey) } } static var lineWidth: Float { get { if let result = shared?.object(forKey: Constants.LineWidthKey) as? Float { return result } return 1000 } set { shared?.set(newValue, forKey: Constants.LineWidthKey) } } static var textMatchAutoSelection: Bool { get { if let result = shared?.object(forKey: Constants.TextMatchAutoSelection) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.TextMatchAutoSelection) } } static var continuousSpellChecking: Bool { get { if let result = shared?.object(forKey: Constants.ContinuousSpellChecking) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.ContinuousSpellChecking) } } static var grammarChecking: Bool { get { if let result = shared?.object(forKey: Constants.GrammarChecking) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.GrammarChecking) } } static var smartInsertDelete: Bool { get { if let result = shared?.object(forKey: Constants.SmartInsertDelete) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.SmartInsertDelete) } } static var automaticSpellingCorrection: Bool { get { if let result = shared?.object(forKey: Constants.AutomaticSpellingCorrection) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.AutomaticSpellingCorrection) } } static var automaticQuoteSubstitution: Bool { get { if let result = shared?.object(forKey: Constants.AutomaticQuoteSubstitution) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.AutomaticQuoteSubstitution) } } static var automaticDataDetection: Bool { get { if let result = shared?.object(forKey: Constants.AutomaticDataDetection) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.AutomaticDataDetection) } } static var automaticLinkDetection: Bool { get { if let result = shared?.object(forKey: Constants.AutomaticLinkDetection) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.AutomaticLinkDetection) } } static var automaticTextReplacement: Bool { get { if let result = shared?.object(forKey: Constants.AutomaticTextReplacement) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.AutomaticTextReplacement) } } static var automaticDashSubstitution: Bool { get { if let result = shared?.object(forKey: Constants.AutomaticDashSubstitution) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.AutomaticDashSubstitution) } } static var isHiddenSidebar: Bool { get { if let result = shared?.object(forKey: Constants.HideSidebar) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.HideSidebar) } } static var shouldFocusSearchOnESCKeyDown: Bool { get { if let result = UserDefaults.standard.object(forKey: Constants.shouldFocusSearchOnESCKeyDown) as? Bool { return result } return true } set { UserDefaults.standard.set(newValue, forKey: Constants.shouldFocusSearchOnESCKeyDown) } } static var automaticConflictsResolution: Bool { get { if let result = UserDefaults.standard.object(forKey: Constants.AutomaticConflictsResolution) as? Bool { return result } return false } set { UserDefaults.standard.set(newValue, forKey: Constants.AutomaticConflictsResolution) } } static var useTextBundleMetaToStoreDates: Bool { get { if let result = UserDefaults.standard.object(forKey: Constants.UseTextBundleToStoreDates) as? Bool { return result } return false } set { UserDefaults.standard.set(newValue, forKey: Constants.UseTextBundleToStoreDates) } } static var showInMenuBar: Bool { get { if let result = shared?.object(forKey: Constants.ShowInMenuBar) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.ShowInMenuBar) } } static var fileContainer: NoteContainer { get { #if SHARE_EXT let defaults = UserDefaults.init(suiteName: "group.es.fsnot.user.defaults") if let result = defaults?.object(forKey: Constants.SharedContainerKey) as? Int, let container = NoteContainer(rawValue: result) { return container } #endif if let result = shared?.object(forKey: Constants.NoteContainer) as? Int, let container = NoteContainer(rawValue: result) { return container } return .none } set { #if os(iOS) UserDefaults.init(suiteName: "group.es.fsnot.user.defaults")?.set(newValue.rawValue, forKey: Constants.SharedContainerKey) #endif shared?.set(newValue.rawValue, forKey: Constants.NoteContainer) } } static var fileFormat: NoteType { get { return .Markdown } set { shared?.set(newValue.tag, forKey: Constants.NoteType) } } static var noteExtension: String { get { if let result = shared?.object(forKey: Constants.NoteExtension) as? String { return result } return "markdown" } set { shared?.set(newValue, forKey: Constants.NoteExtension) } } static var previewFontSize: Int { get { if let result = shared?.object(forKey: Constants.PreviewFontSize) as? Int { return result } return 11 } set { shared?.set(newValue, forKey: Constants.PreviewFontSize) } } static var hidePreviewImages: Bool { get { if let result = shared?.object(forKey: Constants.HidePreviewImages) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.HidePreviewImages) } } static var masterPasswordHint: String { get { if let hint = shared?.object(forKey: Constants.MasterPasswordHint) as? String { return hint } return String() } set { shared?.set(newValue, forKey: Constants.MasterPasswordHint) } } static var lockOnSleep: Bool { get { if let result = shared?.object(forKey: Constants.LockOnSleep) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.LockOnSleep) } } static var lockOnScreenActivated: Bool { get { if let result = shared?.object(forKey: Constants.LockOnScreenActivated) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.LockOnScreenActivated) } } static var lockOnUserSwitch: Bool { get { if let result = shared?.object(forKey: Constants.LockAfterUserSwitch) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.LockAfterUserSwitch) } } static var allowTouchID: Bool { get { if NSClassFromString("NSTouchBar") == nil { return false } if let result = shared?.object(forKey: Constants.AllowTouchID) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.AllowTouchID) } } static var hideDate: Bool { get { if let result = shared?.object(forKey: Constants.HideDate) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.HideDate) } } static var indentUsing: Int { get { if let result = shared?.integer(forKey: Constants.IndentUsing) { return result } return 0 } set { shared?.set(newValue, forKey: Constants.IndentUsing) } } static var firstLineAsTitle: Bool { get { if let result = shared?.object(forKey: Constants.FirstLineAsTitle) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.FirstLineAsTitle) } } static var marginSize: Float { get { if let result = shared?.object(forKey: Constants.MarginSizeKey) as? Float { return result } return 20 } set { shared?.set(newValue, forKey: Constants.MarginSizeKey) } } static var gitStorage: URL? { get { if let repositories = shared?.url(forKey: Constants.GitStorage) { if !FileManager.default.fileExists(atPath: repositories.path) { try? FileManager.default.createDirectory(at: repositories, withIntermediateDirectories: true, attributes: nil) } return repositories } if let applicationSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first { let repositories = applicationSupport.appendingPathComponent("Repositories") if !FileManager.default.fileExists(atPath: repositories.path) { try? FileManager.default.createDirectory(at: repositories, withIntermediateDirectories: true, attributes: nil) } return repositories } return nil } set { shared?.set(newValue, forKey: Constants.GitStorage) } } static var gitUsername: String? { get { if let result = shared?.object(forKey: Constants.GitUsername) as? String { if result.count == 0 { return nil } return result } return nil } set { shared?.set(newValue, forKey: Constants.GitUsername) } } static var gitPassword: String? { get { if let result = shared?.object(forKey: Constants.GitPassword) as? String { if result.count == 0 { return nil } return result } return nil } set { shared?.set(newValue, forKey: Constants.GitPassword) } } static var gitOrigin: String? { get { if let result = shared?.object(forKey: Constants.GitOrigin) as? String { if result.count == 0 { return nil } return result } return nil } set { shared?.set(newValue, forKey: Constants.GitOrigin) } } static var snapshotsInterval: Int { get { if let interval = shared?.object(forKey: Constants.SnapshotsInterval) as? Int { return interval } return self.DefaultSnapshotsInterval } set { shared?.set(newValue, forKey: Constants.SnapshotsInterval) } } static var pullInterval: Int { get { if let interval = shared?.object(forKey: Constants.PullInterval) as? Int { return interval } return 10 } set { shared?.set(newValue, forKey: Constants.PullInterval) } } static var snapshotsIntervalMinutes: Int { get { if let interval = shared?.object(forKey: Constants.SnapshotsIntervalMinutes) as? Int { return interval } return self.DefaultSnapshotsIntervalMinutes } set { shared?.set(newValue, forKey: Constants.SnapshotsIntervalMinutes) } } static var backupManually: Bool { get { if let returnMode = shared?.object(forKey: Constants.BackupManually) as? Bool { return returnMode } else { return true } } set { shared?.set(newValue, forKey: Constants.BackupManually) } } static var fullScreen: Bool { get { if let result = shared?.object(forKey: Constants.FullScreen) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.FullScreen) } } static var inlineTags: Bool { get { if let result = shared?.object(forKey: Constants.InlineTags) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.InlineTags) } } static var showWelcome: Bool { get { if let result = shared?.object(forKey: Constants.Welcome) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.Welcome) } } static var mathJaxPreview: Bool { get { if let result = shared?.object(forKey: Constants.MathJaxPreview) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.MathJaxPreview) } } static var sidebarVisibilityInbox: Bool { get { if let result = shared?.object(forKey: "sidebarVisibilityInbox") as? Bool { return result } return true } set { shared?.set(newValue, forKey: "sidebarVisibilityInbox") } } static var sidebarVisibilityNotes: Bool { get { if let result = shared?.object(forKey: "sidebarVisibilityNotes") as? Bool { return result } return true } set { shared?.set(newValue, forKey: "sidebarVisibilityNotes") } } static var sidebarVisibilityTodo: Bool { get { if let result = shared?.object(forKey: "sidebarVisibilityTodo") as? Bool { return result } return true } set { shared?.set(newValue, forKey: "sidebarVisibilityTodo") } } static var sidebarVisibilityUntagged: Bool { get { if let result = shared?.object(forKey: "sidebarVisibilityUntagged") as? Bool { return result } return true } set { shared?.set(newValue, forKey: "sidebarVisibilityUntagged") } } static var sidebarVisibilityTrash: Bool { get { if let result = shared?.object(forKey: "sidebarVisibilityTrash") as? Bool { return result } return true } set { shared?.set(newValue, forKey: "sidebarVisibilityTrash") } } static var crashedLastTime: Bool { get { if let result = shared?.object(forKey: Constants.CrashedLastTime) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.CrashedLastTime) } } static var lastNews: Date? { get { if let sync = shared?.object(forKey: "lastNews") { return sync as? Date } else { return nil } } set { shared?.set(newValue, forKey: "lastNews") } } static var naming: SettingsFilesNaming { get { if let result = shared?.object(forKey: "naming") as? Int, let settings = SettingsFilesNaming(rawValue: result) { return settings } return .autoRename } set { shared?.set(newValue.rawValue, forKey: "naming") } } static var autoInsertHeader: Bool { get { if let result = shared?.object(forKey: Constants.AutoInsertHeader) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.AutoInsertHeader) } } static var nonContiguousLayout: Bool { get { if let result = shared?.object(forKey: Constants.NonContiguousLayout), let data = result as? Bool { return data } return true } set { shared?.set(newValue, forKey: Constants.NonContiguousLayout) } } static var codeBlocksWithSyntaxHighlighting: Bool { get { if let result = shared?.object(forKey: Constants.CodeBlocksWithSyntaxHighlighting) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.CodeBlocksWithSyntaxHighlighting) } } static var lastScreenX: Int? { get { if let value = shared?.object(forKey: Constants.LastScreenX) as? Int { return value } return nil } set { shared?.set(newValue, forKey: Constants.LastScreenX) } } static var lastScreenY: Int? { get { if let value = shared?.object(forKey: Constants.LastScreenY) as? Int { return value } return nil } set { shared?.set(newValue, forKey: Constants.LastScreenY) } } static var recentSearches: [String]? { get { if let value = shared?.array(forKey: Constants.RecentSearches) as? [String] { return value } return nil } set { shared?.set(newValue, forKey: Constants.RecentSearches) } } static var searchHighlight: Bool { get { if let result = shared?.object(forKey: Constants.SearchHighlight) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.SearchHighlight) } } static var autoVersioning: Bool { get { if let result = shared?.object(forKey: Constants.AutoVersioning) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.AutoVersioning) } } static var iCloudDrive: Bool { get { if let result = shared?.object(forKey: Constants.iCloudDrive) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.iCloudDrive) } } static var customWebServer: Bool { get { if let result = shared?.object(forKey: Constants.CustomWebServer) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.CustomWebServer) } } static var sftpHost: String { get { if let result = shared?.object(forKey: Constants.SftpHost) as? String { return result } return "" } set { shared?.set(newValue, forKey: Constants.SftpHost) } } static var sftpPort: Int32 { get { if let result = shared?.object(forKey: Constants.SftpPort) as? Int32 { return result } return 22 } set { shared?.set(newValue, forKey: Constants.SftpPort) } } static var sftpUsername: String { get { if let result = shared?.object(forKey: Constants.SftpUsername) as? String { return result } return "" } set { shared?.set(newValue, forKey: Constants.SftpUsername) } } static var sftpPassword: String { get { if let result = shared?.object(forKey: Constants.SftpPassword) as? String { return result } return "" } set { shared?.set(newValue, forKey: Constants.SftpPassword) } } static var sftpPath: String? { get { if let result = shared?.object(forKey: Constants.SftpPath) as? String { if result.count == 0 { return nil } let suffix = result.hasSuffix("/") ? "" : "/" return result + suffix } return nil } set { shared?.set(newValue, forKey: Constants.SftpPath) } } static var sftpPassphrase: String { get { if let result = shared?.object(forKey: Constants.SftpPasspharse) as? String { return result } return "" } set { shared?.set(newValue, forKey: Constants.SftpPasspharse) } } static var sftpWeb: String? { get { if let result = shared?.object(forKey: Constants.SftpWeb) as? String { if result.count == 0 { return nil } if result.last != "/" { return result + "/" } return result } return nil } set { shared?.set(newValue, forKey: Constants.SftpWeb) } } static var sftpAccessData: Data? { get { return shared?.data(forKey: Constants.SftpKeysAccessData) } set { shared?.set(newValue, forKey: Constants.SftpKeysAccessData) } } static var sftpUploadBookmarksData: Data? { get { return shared?.data(forKey: Constants.SftpUploadBookmarksData) } set { shared?.set(newValue, forKey: Constants.SftpUploadBookmarksData) } } static var apiBookmarksData: Data? { get { return shared?.data(forKey: Constants.ApiBookmarksData) } set { shared?.set(newValue, forKey: Constants.ApiBookmarksData) } } static var gitPrivateKeyData: Data? { get { return shared?.data(forKey: Constants.GitPrivateKeyData) } set { shared?.set(newValue, forKey: Constants.GitPrivateKeyData) } } static var gitPassphrase: String { get { if let result = shared?.object(forKey: Constants.GitPasspharse) as? String { return result } return "" } set { shared?.set(newValue, forKey: Constants.GitPasspharse) } } static var uploadKey: String { get { if let result = global.object(forKey: Constants.UploadKey) as? String, result.count > 0 { return result } let key = String.random(length: 20) global.set(key, forKey: Constants.UploadKey) return key } set { global.set(newValue, forKey: Constants.UploadKey) } } static var deprecatedUploadKey: String? { get { if let result = shared?.object(forKey: Constants.UploadKey) as? String, result.count > 0 { return result } return nil } set { shared?.set(newValue, forKey: Constants.UploadKey) } } static var clickableLinks: Bool { get { if let highlight = shared?.object(forKey: Constants.ClickableLinks) as? Bool { return highlight } #if os(iOS) return true #else return false #endif } set { shared?.set(newValue, forKey: Constants.ClickableLinks) } } static var trashURL: URL? { get { if let trashUrl = shared?.url(forKey: Constants.TrashKey) { return trashUrl } return nil } set { shared?.set(newValue, forKey: Constants.TrashKey) } } static var separateRepo: Bool { get { if let result = shared?.object(forKey: Constants.SeparateRepo) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.SeparateRepo) } } static var askCommitMessage: Bool { get { if let result = shared?.object(forKey: Constants.AskCommitMessage) as? Bool { return result } return false } set { shared?.set(newValue, forKey: Constants.AskCommitMessage) } } static var lastCommitMessage: String? { get { if let result = shared?.object(forKey: Constants.LastCommitMessage) as? String, result.count > 0 { return result } return nil } set { shared?.set(newValue, forKey: Constants.LastCommitMessage) } } static var lightCodeTheme: String { get { if let theme = UserDefaults.standard.object(forKey: Constants.codeTheme) as? String { return theme } return "github" } set { UserDefaults.standard.set(newValue, forKey: Constants.codeTheme) } } static var projects: [URL] { get { guard let defaults = UserDefaults.init(suiteName: "group.es.fsnot.user.defaults") else { return [] } if let data = defaults.data(forKey: Constants.ProjectsKeyNew), let urls = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, NSURL.self], from: data) as? [URL] { return urls } return [] } set { guard let defaults = UserDefaults.init(suiteName: "group.es.fsnot.user.defaults") else { return } if let data = try? NSKeyedArchiver.archivedData(withRootObject: newValue, requiringSecureCoding: true) { defaults.set(data, forKey: Constants.ProjectsKeyNew) } } } static var maxChildDirs: Int { get { if let returnFontSize = shared?.object(forKey: Constants.MaxChildDirs), let value = returnFontSize as? Int { if value < 200 { return 200 } return value } return 200 } set { shared?.set(newValue, forKey: Constants.CodeFontSizeKey) } } #if !SHARE_EXT static var codeTheme: EditorTheme { get { guard let raw = UserDefaults.standard.string(forKey: Constants.codeTheme), let theme = EditorTheme(rawValue: raw) else { return .atomOne } return theme } set { UserDefaults.standard.set(newValue.rawValue, forKey: Constants.codeTheme) } } #endif static var isFirstLaunch: Bool { get { if let result = shared?.object(forKey: Constants.IsFirstLaunch) as? Bool { return result } return true } set { shared?.set(newValue, forKey: Constants.IsFirstLaunch) } } static var italic: String { get { if let returnFontName = shared?.object(forKey: Constants.italicKey) as? String { return returnFontName } return "*" } set { shared?.set(newValue, forKey: Constants.italicKey) } } static var bold: String { get { if let returnFontName = shared?.object(forKey: Constants.boldKey) as? String { return returnFontName } return "__" } set { shared?.set(newValue, forKey: Constants.boldKey) } } } ================================================ FILE: FSNotesCore/ViewController+WebApi.swift ================================================ // // ViewController+WebApi.swift // FSNotes // // Created by Oleksandr Hlushchenko on 24.05.2023. // Copyright © 2023 Oleksandr Hlushchenko. All rights reserved. // #if os(OSX) import Cocoa #else import UIKit #endif extension ViewController { public func deleteAPI(note: Note, completion: (() -> Void)? = nil) { guard let noteId = note.apiId else { return } let api = UserDefaultsManagement.apiPath let boundary = generateBoundaryString() let session = URLSession.shared let url = URL(string: "\(api)?method=delete")! var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") let key = UserDefaultsManagement.uploadKey let parameters = ["key": key, "note_id": noteId] do { request.httpBody = try createBody(with: parameters, filePathKey: "file", urls: [], boundary: boundary) } catch { print("Request creation: \(error)") return } let task = session.dataTask(with: request) { data, response, error in guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) && error == nil else { self.showAlert(message: "FSNotes server is down at this moment, please try later") return } guard let responseData = data else { self.showAlert(message: "Empty response") return } let decoder = JSONDecoder() if let api = try? decoder.decode(APIResponse.self, from: responseData) { if let msg = api.error { self.showAlert(message: msg) if msg == "Wrong key" { note.apiId = nil note.project.saveWebAPI() completion?() } } else if api.id != nil { note.apiId = nil note.project.saveWebAPI() completion?() } } } task.resume() } public func createAPI(note: Note, completion: ((_ url: URL?) -> Void)? = nil) { let web = UserDefaultsManagement.webPath let api = UserDefaultsManagement.apiPath let dst = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("Upload") try? FileManager.default.removeItem(at: dst) guard let localURL = MPreviewView.buildPage(for: note, at: dst, web: true) else { return } let zipUrl = localURL.deletingLastPathComponent().appendingPathComponent(note.getLatinName()).appendingPathExtension("zip") let privateKey = UserDefaultsManagement.uploadKey var parameters = ["key": privateKey] if let noteId = note.apiId { parameters["note_id"] = noteId } let boundary = generateBoundaryString() let method = note.apiId != nil ? "update" : "create" let url = URL(string: "\(api)?method=\(method)")! let session = URLSession.shared var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") var urls = [URL]() let items = note.content.getImagesAndFiles() for item in items { urls.append(item.url) } urls.append(localURL) urls.append(zipUrl) guard let body = try? createBody(with: parameters, filePathKey: "file", urls: urls, boundary: boundary) else { return } request.httpBody = body let task = session.dataTask(with: request) { data, response, error in guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) && error == nil else { self.showAlert(message: "FSNotes server is down at this moment, please try later") return } guard let responseData = data else { self.showAlert(message: "Empty response") return } let decoder = JSONDecoder() if let api = try? decoder.decode(APIResponse.self, from: responseData) { if let msg = api.error { self.showAlert(message: msg) } else if let noteId = api.id { note.apiId = noteId note.project.saveWebAPI() let resultUrl = "\(web)\(noteId)/" let url = URL(string: resultUrl)! completion?(url) } } } task.resume() } private func createBody(with parameters: [String: String]? = nil, filePathKey: String, urls: [URL], boundary: String) throws -> Data { var body = Data() parameters?.forEach { (key, value) in body.append("--\(boundary)\r\n") body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n") body.append("\(value)\r\n") } var urlNum = 0 for url in urls { urlNum += 1 let filename = url.lastPathComponent let data = try Data(contentsOf: url) body.append("--\(boundary)\r\n") body.append("Content-Disposition: form-data; name=\"entity_\(urlNum)\"; filename=\"\(filename)\"\r\n") body.append("Content-Type: \(url.mimeType)\r\n\r\n") body.append(data) body.append("\r\n") } body.append("--\(boundary)--\r\n") return body } private func generateBoundaryString() -> String { return "Boundary-\(UUID().uuidString)" } } ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2018 Oleksandr Glushchenko 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: Logo/License ================================================ MIT License Copyright (c) 2018 Baran Pirinçal 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: Podfile ================================================ use_frameworks! MAC_TARGET_VERSION = '10.14' IOS_TARGET_VERSION = '14' def mac_pods pod 'MASShortcut', :git => 'https://github.com/glushchenko/MASShortcut.git', :branch => 'master' end def ios_pods pod 'SSZipArchive', :git => 'https://github.com/glushchenko/ZipArchive.git', :branch => 'master' pod 'DropDown', '2.3.13' pod 'SwipeCellKit', :git => 'https://github.com/glushchenko/SwipeCellKit.git', :branch => 'develop' pod 'CropViewController' end def common_pods pod 'libcmark_gfm', :git => 'https://github.com/glushchenko/libcmark_gfm', :branch => 'master' pod 'RNCryptor', '~> 5.1.0' pod 'SSZipArchive', :git => 'https://github.com/glushchenko/ZipArchive.git', :branch => 'master' pod 'Punycode' end def framework_pods pod 'SwiftLint', '~> 0.30.0' end target 'FSNotes' do platform :osx, MAC_TARGET_VERSION mac_pods common_pods end target 'FSNotes (iCloud)' do platform :osx, MAC_TARGET_VERSION mac_pods common_pods end target 'FSNotes iOS' do platform :ios, IOS_TARGET_VERSION common_pods ios_pods end target 'FSNotes iOS Share Extension' do platform :ios, IOS_TARGET_VERSION pod 'RNCryptor', '~> 5.1.0' pod 'SSZipArchive', :git => 'https://github.com/glushchenko/ZipArchive.git', :branch => 'master' end post_install do |installer| installer.pods_project.targets.each do |target| if target.name == 'cmark-gfm-swift-macOS' source_files = target.source_build_phase.files dummy = source_files.find do |file| file.file_ref.name == 'scanners.re' end source_files.delete dummy dummyM = source_files.find do |file| file.file_ref.name == 'module.modulemap' end source_files.delete dummyM puts "Deleting source file #{dummy.inspect} from target #{target.inspect}." end if target.name == 'libcmark_gfm-macOS' || target.name == 'MASShortcut' || target.name == 'SSZipArchive-macOS' || target.name == 'RNCryptor-macOS' target.build_configurations.each do |config| config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.14' end end if target.name == 'SSZipArchive-iOS' || target.name == 'RNCryptor-iOS' || target.name == 'DropDown' || target.name == 'DKCamera' || target.name == 'CropViewController' target.build_configurations.each do |config| config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' end end end end ================================================ FILE: README.md ================================================ # FSNotes [简体中文](README_zh_CN.md) [繁體中文](README_zh_TW.md) FSNotes is modern notes manager for macOS and iOS. ## macOS app macOS FSNotes ### Key features - **Markdown-first**. Also supports any plaintext files. - **Fast and lightweight**. Works smoothly with 10k+ files. - **Access anywhere**. Sync with iCloud Drive or Dropbox. - **Multi-folder** storage. - **Keyboard-centric**. - **Syntax highlighting** within code blocks. Supports over 30 programming languages. - **In-line image** support. - Organize with **tags**. - **Cross-note links** using `[[double brackets]]`. - **Elastic two-pane view**. - **External editor** support (changes seamless live sync with UI). - **Pin** important notes. - **Quickly copy notes** to the clipboard. - **Dark mode**. - AES-256 **encryption**. - **Mermaid and MathJax** support. - Optional **Git versioning** and **backups**. --- ## iOS app FSNotes for iOS FSNotes for iOS ### Key features - **Sync via iCloud Drive**. - **3D Touch** and **configurable keyboard**. - **TextBundle** and **EncryptedTextBundle** containers. - **Pinned** notes kept in sync with the desktop app. - **Dynamic fonts**. - **Dark mode**. - **Sharing** extension. - **Encrypted note** support. - **Encrypted folders** support. - **Git** integration. - **Web Pages** Creation. ## License FSNotes is written in **Swift 5** and is open source (MIT license). ================================================ FILE: README_zh_CN.md ================================================ # FSNotes [English](README.md) [繁體中文](README_zh_TW.md) FSNotes是适用于 macOS 和 iOS 的现代笔记管理器。 ## macOS 应用 macOS FSNotes ### 主要功能 - **优先支持 Markdown**。也支持任何纯文本文件。 - **快速且轻量**。能够流畅处理 10k+ 个文件。 - **随时随地访问**。与 iCloud Drive 或 Dropbox 同步。 - **多文件夹**存储。 - **键盘为中心**。受 [nvalt](https://brettterpstra.com/projects/nvalt/) 启发的控件和快捷键。 - **代码块内语法高亮**。支持超过 170 种编程语言。 - **内联图片**支持。 - 使用**标签**进行组织。 - 使用 `[[双括号]]` 进行**跨笔记链接**。 - **弹性两窗格视图**。选择垂直或水平布局。 - 支持**外部编辑器**(更改会与 UI 实时同步)。 - **置顶**重要笔记。 - **快速复制笔记**到剪贴板。 - **暗黑模式**。 - AES-256 **加密**。 - **Mermaid 和 MathJax** 支持。 - 可选的**Git 版本控制**和**备份**。 --- ## iOS 应用 FSNotes for iOS FSNotes for iOS ### 主要功能 - 通过 iCloud Drive 进行**同步**。 - **3D Touch** 和**可配置键盘**。 - **TextBundle** 和 **EncryptedTextBundle** 容器。 - 保持与桌面应用同步的**置顶**笔记。 - **动态字体**。 - **暗黑模式**。 - **分享**扩展。 - **加密笔记**支持。 - **加密文件夹**支持。 - **Git** 集成。 - **网页**创建。 ## 许可证 FSNotes 使用 **Swift 5** 编写,采用 MIT 许可证开源。 ## 特别鸣谢 @zcohan(https://soulver.app)提供了 Soulver 核心框架 https://github.com/soulverteam/SoulverCore,用于简单的内联计算。 ================================================ FILE: README_zh_TW.md ================================================ # FSNotes [English](README.md) [简体中文](README_zh_CN.md) FSNotes是一款適用於 macOS 和 iOS 的現代筆記管理器。 ## macOS 應用程式 macOS FSNotes ### 主要功能 - **Markdown 优先**。也支持任何纯文本文件。 - **快速且輕量**。能夠流暢處理 10k+ 個文件。 - **隨時隨地訪問**。與 iCloud Drive 或 Dropbox 同步。 - **多資料夾**存儲。 - **鍵盤為中心**。受 [nvalt](https://brettterpstra.com/projects/nvalt/) 啟發的控件和快捷鍵。 - **程式碼塊內語法高亮**。支持超過 170 種編程語言。 - **內嵌圖片**支持。 - 使用**標籤**進行組織。 - 使用 `[[雙括號]]` 進行**跨筆記鏈接**。 - **彈性兩窗格視圖**。選擇垂直或水平佈局。 - 支持**外部編輯器**(更改會與 UI 實時同步)。 - **置頂**重要筆記。 - **快速複製筆記**到剪貼板。 - **暗黑模式**。 - AES-256 **加密**。 - **Mermaid 和 MathJax** 支持。 - 可選的**Git 版本控制**和**備份**。 --- ## iOS 應用程式 FSNotes for iOS FSNotes for iOS ### 主要功能 - 通過 iCloud Drive 進行**同步**。 - **3D Touch** 和**可配置鍵盤**。 - **TextBundle** 和 **EncryptedTextBundle** 容器。 - 保持與桌面應用同步的**置頂**筆記。 - **動態字體**。 - **暗黑模式**。 - **分享**擴展。 - **加密筆記**支持。 - **加密資料夾**支持。 - **Git** 集成。 - **網頁**創建。 ## 許可證 FSNotes 使用 **Swift 5** 編寫,採用 MIT 許可證開源。 ## 特別鳴謝 @zcohan(https://soulver.app)提供了 Soulver 核心框架 https://github.com/soulverteam/SoulverCore,用於簡單的內嵌計算。 ================================================ FILE: Resources/Initial/FSNotes - Readme.md ================================================ ## FSNotes – File System Notes Manager FSNotes lets write and store notes in portable and non vendor locked formats, catalog and manage large amounts of data. You can view, edit, and copy data in your favourite external editor and see live results in FSNotes. It's simple and blazing fast! Memorizing [keyboard shortcuts](https://github.com/glushchenko/fsnotes/wiki/Keyboard-Shortcuts) takes some work, but once you have, shortcuts make using FSNotes so much more efficient. ### Key features - Markdown-first. Also supports any plaintext files. - Fast and lightweight. Works smoothly with 10k+ files. - Access anywhere. Sync with iCloud Drive or Dropbox. - Multi-folder storage. - Keyboard-centric. nvalt-inspired controls and shortcuts. - Syntax highlighting within code blocks. Supports over 170 programming languages. - In-line image support. - Organize with tags. ### Screen ![](https://raw.githubusercontent.com/glushchenko/fsnotes/master/code.png) ### And many more - Cross-note links using [[double brackets]]. - Elastic two-pane view. Choose a vertical or horizontal layout. - External editor support (changes seamless live sync with UI). - Pin important notes. - Quickly copy notes to the clipboard. - Dark mode. - Lock sensitive notes with AES-256 encryption. - Mermaid and MathJax support. - Optional Git versioning and backups. - iOS app with sync via iCloud Drive. ### Wiki [Alfred Workflow](https://github.com/glushchenko/fsnotes/wiki/Alfred-Workflow) [Backup & Versioning of Notes](https://github.com/glushchenko/fsnotes/wiki/Backup-&-Versioning-of-Notes) [Containers](https://github.com/glushchenko/fsnotes/wiki/Containers) [Finding & creating documents via URLs](https://github.com/glushchenko/fsnotes/wiki/Finding-&-creating-documents-via-URLs) [Markdown Reference](https://github.com/glushchenko/fsnotes/wiki/Markdown-Reference) [Report Bugs or Request Features](https://github.com/glushchenko/fsnotes/wiki/Report-Bugs-or-Request-Features) [Keyboard Shortcuts](https://github.com/glushchenko/fsnotes/wiki/Keyboard-Shortcuts) Special thanks to [Matt Sephton](https://www.gingerbeardman.com) 👍 ### Author © Oleksandr Hlushchenko and [Contributors](https://github.com/glushchenko/fsnotes/graphs/contributors), 2017–2020 ================================================ FILE: Resources/Initial/FSNotes 4.0 Change Log.textbundle/info.json ================================================ { "transient" : true, "type" : "net.daringfireball.markdown", "creatorIdentifier" : "co.fluder.fsnotes", "version" : 2 } ================================================ FILE: Resources/Initial/FSNotes 4.0 Change Log.textbundle/text.markdown ================================================ # FSNotes 4 is here! ![](assets/128.png) Appreciate the patience while i worked on this update. Thanks to pledgers, testers and all FSNotes community, love you all guys. This is biggest update in FSNotes history, so i have some news: ## Polished iCloud Drive sync Yep! Now more stable. Fixed bug with data missing 😱 New folder appears in sidebar without app restarting. 4.0 fixes crashes related to notes import. ## User interface New generation of iPhones with notches supports properly now 🥳 Main controller completely reimplemented with parallax effect. Sidebar opens with flawless animation. New notes and projects adds/removes without tables reloading. ## App speedup The new caching system allows you to launch the application very quick. I have almost 10000 notes and this pretty fast. Projects selection and search speed improved too. ## True dark mode The app uses true, hex black — #000000 — for the majority of the dark theme. This is really good news for iPhones with OLED displays. ## Preview mode FSNotes 4.0 adds new preview mode, that always on. Off course, you can make always off in one tap. Just double tap for edit note or click appropriate icon in navigation bar. Added full screen in landscape. In preview, you even can mark checkboxes 🔥 ## Handoff This is a new feature that i use regularly on iOS and macOS. Open note in FSNotes on iPhone and continue your task on MacBook in one click. I think more info you can read on official site https://support.apple.com/en-us/HT209455 ## Auto renaming Now you can choose in settings — use UUID for files naming or auto rename files by title from first line. ## Projects and tags managements In FSNotes 4.0 you can easily manage your folders and tags. Create, rename and delete folders and tags in a couple of tap. Just long press item in sidebar or tap on project/tag title in navigation bar. ## Wikilinks Now you can link one document to another easily. Look for the document icon in the editor toolbar. ## Fin Thanks for choosing FSNotes. — Oleksandr ================================================ FILE: Resources/Initial/FSNotes 4.0 for iOS.textbundle/info.json ================================================ { "transient" : true, "type" : "net.daringfireball.markdown", "creatorIdentifier" : "co.fluder.fsnotes", "version" : 2 } ================================================ FILE: Resources/Initial/FSNotes 4.0 for iOS.textbundle/text.markdown ================================================ # Hello, folks! ![](assets/128.png) FSNotes is **modern** notes manager for macOS and iOS. This app respects open formats like _Markdown_ and _GitHub Flavored Markdown_, so you can easily write documents on iPhone and MacBook. > You can quote > Multiple lines Insert #tags and #multi/level/tags Just write "#" and autocomplete existed tag. _Use code blocks:_ ```php echo "Hello world"! ``` ## Images Lists, numbered lists and todo: - Lists item 1. First Item 2. Second Item — - [x] Pay bills - [ ] Buy water And WikiLinks [[WikiLinks with emoji 😎]] ### MermaidJS allows you to include diagrams in your note ```mermaid sequenceDiagram participant Alice participant Bob Alice->>John: Hello John, how are you? loop Healthcheck John->>John: Fight against hypochondria end Note right of John: Rational thoughts
prevail! John-->>Alice: Great! John->>Bob: How about you? Bob-->>John: Jolly good! ``` ### And MathJax allows you to include mathematics Documentation: https://www.mathjax.org When $a \ne 0$, there are two solutions to \\(ax^2 + bx + c = 0\\) and they are $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$ ## Containers Personally i recommend TextBundle containers , FSNotes implements it fully, so go to Settings -> Default container and check "textbundle". You can read spec here: http://textbundle.org/ ## Author Thanks for choosing FSNotes. — Oleksandr | ================================================ FILE: Resources/Initial/FSNotes 5.0 Change Log.textbundle/info.json ================================================ { "transient" : true, "type" : "net.daringfireball.markdown", "creatorIdentifier" : "co.fluder.fsnotes", "version" : 2 } ================================================ FILE: Resources/Initial/FSNotes 5.0 Change Log.textbundle/text.md ================================================ # Meet FSNotes 5 for Mobile! ## Interface Transitions between controllers have become smoother and more native, this applies to both the main screen and the editor. Access to the settings has become easier. To call up the action menu above the note, just hold your finger on the note (the sidebar must be closed). ![](assets/8822c62e-16ef-4f4c-b397-55d2d79e86fb.jpg) ## Versioning If you accidentally erased part of the note, it doesn't matter. Now at any time you can return to the state until you entered the editor and roll back the changes, and you can also create a new version of the file at any time. Of course, we also store the history of images, and certainly in the "History" menu you can clean everything in one click. ![](assets/9ebef6e8-741f-4fd7-b6fb-0fef6d471c44.jpg) ## Inserting and working with images New ImagePicker! - Gestures to move between images left to right if you touch the left or right edge. - Framing. - Sharing and deleting in one click. ![](assets/add6cab8-34d1-49f6-b3f3-316b0b3c5ebc.jpg) ## Editor improvements - Setting up line spacing - New fonts - Large indents from the edges of the screen - Improved editor performance - Keyboard and controller close swipes now work from the center of the editor. - Configurable themes and fonts for code blocks ## A diary New features for keeping a diary - now you can change the creation date with two taps. Just click the three dots above the editor and select "Created Date" ![](assets/a4bdc32a-ae63-4d39-a4ef-458639b1d3f1.jpg) ## Troubleshooting - Inconsistency of the selected note being selected in the list - Twitching images in note list - Crashes while typing ## All sorts of useful little things - Create duplicate notes in two taps. - Added the ability to share an encrypted note. - Folders can now be opened in Files.app. - Share menu now packs notes into a .zip archive. - You can easily disable iCloud Drive in settings (not recommended). - Three minuses in the editor to add the hr tag to the preview - Added empty cart menu. - Added the ability to select the naming format for new notes (as in the macOS version of the program). - Configurable sidebar items And also much more :-) ================================================ FILE: Resources/Initial/Meet FSNotes 6.textbundle/info.json ================================================ { "transient" : true, "type" : "net.daringfireball.markdown", "creatorIdentifier" : "co.fluder.fsnotes", "version" : 2 } ================================================ FILE: Resources/Initial/Meet FSNotes 6.textbundle/text.markdown ================================================ # Meet FSNotes 6! ## New design ![](assets/2023-04-08%20Unmasked%20Icon,%20Light%20-%20FSNotes.png) The biggest design change in the history of the program. The new FSNotes adheres even more to Apple's human interface guidelines. Branded icons, menus, toolbars, headers, animations and smooth transitions. Everything has become closer to the familiar Apple system programs. I want to give a huge thanks to my friend Dylan Seeger for his valuable advice. Thanks to his ticketing, you see this update like this. ## Versioning with git The last version added versioning, but I didn't think it was enough. Therefore, meet git! If you are a programmer, you probably know what it is, but if you don't, you can learn about it here: https://en.wikipedia.org/wiki/Git. You can create a git repository for each folder and commit your notes and send the changes to your server. Changes are pulled up when the app activates and you can also set it to do it every 30 seconds automatically. ![](assets/IMG_1882.jpeg) ## Encrypted folders Folder encryption appeared in the desktop program a year ago, now I'm adding the same feature in the mobile version as well. The first step you should take is to set a master password in the settings. Then continue to each folder specifically by selecting it in the sidebar with a long press or in the navigation menu. The selected folder with all the notes will be encrypted with AES-256. ![](assets/encrypt-menu.jpeg) ## Application icons The old icon by Roman Kliuchkovych was beautiful, but time is of the essence, for the sixth version the new icon was drawn by Dylan Seeger. You can find his website in the settings! I often use the main wallpaper color black, so there is also an all-black version. Mine is set to look like this: ![](assets/black-icon.jpeg) ## Improvements in the editor Even more actions are available directly within the editor, thanks to the new toolbar you can now quickly create a new note or go to search on the main screen. ## Web sharing Sometimes there is a need to quickly share a note. However, not everyone has markdown-enabled editors, much less textbundle. For this purpose, I have added publishing to the web. With a long click on a note in the list, bring up the context menu and select the "Create Web Page" menu item. The note will open in your browser and the link will be copied to your clipboard. So far there is no heavy load and I have not deleted the notes, but over time I think they will be temporary, with a shelf life of 1 month. ![](assets/web-sharing.png) ## Fixes Earlier there were some problems with encryption of folders, app crashes, and previewing notes. All of this has been fixed. New bugs may have been added, of course. -- Oleksandr, Kharkiv, Ukraine ================================================ FILE: Resources/Initial/Meet FSNotes 7.textbundle/info.json ================================================ { "transient" : true, "type" : "net.daringfireball.markdown", "creatorIdentifier" : "co.fluder.fsnotes", "version" : 2 } ================================================ FILE: Resources/Initial/Meet FSNotes 7.textbundle/text.markdown ================================================ # Meet FSNotes 7 ## Hello ![](assets/logo.png) Hi folks! I keep improving the app, adding new features and removing everything that has become outdated. I simply can’t make peace with bad code, bad design, or a bad user interface. So I apologize to users if I suddenly removed a feature you were used to — it means only one thing: I can’t implement it at a level of quality I’m satisfied with. FSNotes 7 supports iOS 18 and iOS 26. ## New Apple redesigned the system UI once again, which meant updating the app’s design yet again — as a result, all iOS 26 users will get the new Liquid Glass design. Fresh menus, search at the bottom of the controller, new toolbars, and more... Editor performance has improved once again, thanks to rewriting the Syntax Highlighter from scratch in Swift. It now works natively and fast. You can now add not only images but also files directly in the editor, just like in the desktop app. A full-fledged search function has also been added within the note. A huge number of bugs have been fixed, and much more. All in all — let’s move forward and enjoy a new experience with FSNotes 7! -- Oleksandr, Ukraine, Kharkiv 12.01.2026 ================================================ FILE: Resources/MPreview.bundle/index.html ================================================ {TITLE} {INLINE_CSS} {MATH_JAX_JS} {NOTE_BODY} ================================================ FILE: Resources/MPreview.bundle/js/tex-mml-chtml.js ================================================ !function(r){var n={};function i(t){if(n[t])return n[t].exports;var e=n[t]={i:t,l:!1,exports:{}};return r[t].call(e.exports,e,e.exports,i),e.l=!0,e.exports}i.m=r,i.c=n,i.d=function(t,e,r){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},i.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(i.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)i.d(r,n,function(t){return e[t]}.bind(null,n));return r},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s=230)}([function(t,s,e){"use strict";var n,r=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),c=this&&this.__assign||function(){return(c=Object.assign||function(t){for(var e,r=1,n=arguments.length;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},m=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},v=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0 mjx-mid"]={"margin-top":this.em(-p/2),"margin-bottom":this.em(-p/2)}}l&&(h["border-top-width"]=this.em0(l-.03)),u&&(h["border-bottom-width"]=this.em0(u-.03),t[f+"mjx-stretchy-v"+e+" > mjx-end"]={"margin-top":this.em(-u)}),Object.keys(h).length&&(t[f+"mjx-stretchy-v"+e+" > mjx-ext"]=h)},s.prototype.addDelimiterVPart=function(t,e,r,n,i){if(!i)return 0;var o=this.getDelimiterData(i),a=(r-o[2])/2,s={content:this.charContent(i)};return"ext"!==n?s.padding=this.padding(o,a):a&&(s["padding-left"]=this.em0(a)),t[this.cssRoot+"mjx-stretchy-v"+e+" mjx-"+n+" mjx-c::before"]=s,o[0]+o[1]},s.prototype.addDelimiterHStyles=function(t,e,r){var n=v(r.stretch,4),i=n[0],o=n[1],a=n[2],s=n[3];this.addDelimiterHPart(t,e,"beg",i),this.addDelimiterHPart(t,e,"ext",o,!(i||a)),this.addDelimiterHPart(t,e,"end",a),s&&(this.addDelimiterHPart(t,e,"mid",s),t[this.cssRoot+"mjx-stretchy-h"+e+" > mjx-ext"]={width:"50%"})},s.prototype.addDelimiterHPart=function(t,e,r,n,i){if(void 0===i&&(i=!1),!n)return 0;var o=this.getDelimiterData(n),a=o[3],s={content:a&&a.c?'"'+a.c+'"':this.charContent(n)};"ext"===r&&!i||(s.padding=this.padding(o,0,-o[2])),t[this.cssRoot+"mjx-stretchy-h"+e+" mjx-"+r+" mjx-c::before"]=s},s.prototype.addCharStyles=function(t,e,r,n,i){var o=v(n,4),a=(o[0],o[1],o[2]),s=o[3];if(!this.options.adaptiveCSS||s.used){var c={},l="mjx-c"+this.charSelector(r),u=this.cssRoot;c.padding=this.padding(n,0,s.ic||0);var h=s.c?'"'+s.c+'"':this.charContent(r);i.get(r)!==h&&(i.has(r)||s.c?t[u+e+" "+l+"::before"]={content:h}:(t[u+l+"::before"]={content:h},i.set(r,h))),void 0!==s.f&&(c["font-family"]="MJXZERO, MJXTEX"+(s.f?"-"+s.f:""));var f=(e?e+" ":"")+l;if(t[u+f]=c,s.ic){var p=v([u+"mjx-","[noIC]"+f+":last-child"],2),d=p[0],m=p[1];t[d+"mi"+m]=t[d+"mo"+m]={"padding-right":this.em(a)}}}},s.prototype.getDelimiterData=function(t){return this.getChar("-smallop",t)},s.charOptions=function(t,e){return h.charOptions.call(this,t,e)},s.prototype.em=function(t){return o.em(t)},s.prototype.em0=function(t){return o.em(Math.max(0,t))},s.prototype.padding=function(t,e,r){var n=v(t,3),i=n[0],o=n[1];return void 0===e&&(e=0),void 0===r&&(r=0),[i,n[2]+r,o,e].map(this.em0).join(" ")},s.prototype.charContent=function(t){return'"'+(32<=t&&t<=126&&34!==t&&39!==t&&92!==t?String.fromCharCode(t):"\\"+t.toString(16).toUpperCase())+'"'},s.prototype.charSelector=function(t){return".mjx-c"+t.toString(16).toUpperCase()},s.OPTIONS={fontURL:"js/output/chtml/fonts/tex-woff-v2"},s.defaultVariantClasses={},s.defaultStyles={"mjx-c::before":{display:"inline-block",width:0}},s.defaultFonts={"@font-face /* 0 */":{"font-family":"MJXZERO",src:'url("%%URL%%/MathJax_Zero.woff") format("woff")'}},s);function s(t){var e,r;void 0===t&&(t=null);var n=h.call(this)||this;n.cssRoot="";var i=n.constructor;n.options=u.userOptions(u.defaultOptions({},i.OPTIONS),t);try{for(var o=y(Object.keys(i.defaultVariantClasses)),a=o.next();!a.done;a=o.next()){var s=a.value;n.variant[s].classes=i.defaultVariantClasses[s]}}catch(t){e={error:t}}finally{try{a&&!a.done&&(r=o.return)&&r.call(o)}finally{if(e)throw e.error}}return n}r.CHTMLFontData=a,r.AddCSS=function(t,e){var r,n;try{for(var i=y(Object.keys(e)),o=i.next();!o.done;o=i.next()){var a=o.value,s=parseInt(a);Object.assign(c.FontData.charOptions(t,s),e[s])}}catch(t){r={error:t}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}return t}},function(t,u,e){"use strict";var n,r,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),h=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},f=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},r=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};function s(t,e){var r,n;try{for(var i=l(Object.keys(e)),o=i.next();!o.done;o=i.next()){var a=o.value;"__esModule"!==a&&("object"==typeof t[a]&&"object"==typeof e[a]?s(t[a],e[a]):null!==e[a]&&void 0!==e[a]&&(t[a]=e[a]))}}catch(t){r={error:t}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}return t}Object.defineProperty(e,"__esModule",{value:!0}),e.combineConfig=s,e.combineDefaults=function t(e,r,n){var i,o;e[r]||(e[r]={}),e=e[r];try{for(var a=l(Object.keys(n)),s=a.next();!s.done;s=a.next()){var c=s.value;"object"==typeof e[c]&&"object"==typeof n[c]?t(e,c,n[c]):null==e[c]&&null!=n[c]&&(e[c]=n[c])}}catch(t){i={error:t}}finally{try{s&&!s.done&&(o=a.return)&&o.call(a)}finally{if(i)throw i.error}}return e},e.combineWithMathJax=function(t){return s(e.MathJax,t)},void 0===t.MathJax&&(t.MathJax={}),t.MathJax.version||(t.MathJax={version:"3.0.0",_:{},config:t.MathJax}),e.MathJax=t.MathJax}).call(this,r(28))},function(t,e,r){"use strict";var l=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},n=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var n,i,a,o,s,c,h,f=r(0),p=r(6),d=r(21),m=r(4),y=r(12);function v(t,e){void 0===e&&(e=!1);var r=t.match(e?h:c);return r?[r[1].replace(/,/,"."),r[4],r[0].length]:[null,null,0]}function b(t,e,r){"{"!==e&&"}"!==e||(e="\\"+e);var n="{\\bigg"+r+" "+e+"}",i="{\\big"+r+" "+e+"}";return new d.default("\\mathchoice"+n+i+i+i,{},t).mml()}function g(t,e,r){e=e.replace(/^\s+/,y.entities.nbsp).replace(/\s+$/,y.entities.nbsp);var n=t.create("text",e);return t.create("node","mtext",[],r,n)}function M(t,e,r){if(r.match(/^[a-z]/i)&&e.match(/(^|[^\\])(\\\\)*\\[a-z]+$/i)&&(e+=" "),e.length+r.length>t.configuration.options.maxBuffer)throw new m.default("MaxBufferSize","MathJax internal buffer size exceeded; is there a recursive macro call?");return e+r}function O(t,e){for(;0e.length)throw new m.default("IllegalMacroParam","Illegal macro parameter reference");i=M(t,M(t,i,n),e[parseInt(a,10)-1]),n=""}else n+=a}return M(t,i,n)},i.addArgs=M,i.checkEqnEnv=function(t){if(t.stack.global.eqnenv)throw new m.default("ErroneousNestingEq","Erroneous nesting of equation structures");t.stack.global.eqnenv=!0},i.MmlFilterAttribute=function(t,e,r){return r},i.getFontDef=function(t){var e=t.stack.env.font;return e?{mathvariant:e}:{}},i.keyvalOptions=function(t,e,r){var n,i;void 0===e&&(e=null),void 0===r&&(r=!1);var o=function(t){for(var e,r,n,i,o,a={},s=t;s;)e=l(x(s,["=",","]),3),i=e[0],n=e[1],s=e[2],"="===n?(r=l(x(s,[","]),3),o=r[0],n=r[1],s=r[2],o="false"===o||"true"===o?JSON.parse(o):o,a[i]=o):i&&(a[i]=!0);return a}(t);if(e)try{for(var a=u(Object.keys(o)),s=a.next();!s.done;s=a.next()){var c=s.value;if(!e.hasOwnProperty(c)){if(r)throw new m.default("InvalidOption","Invalid optional argument: %1",c);delete o[c]}}}catch(t){n={error:t}}finally{try{s&&!s.done&&(i=a.return)&&i.call(a)}finally{if(n)throw n.error}}return o},e.default=n},function(t,e,r){"use strict";var n,i,o,l=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},u=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},c=this&&this.__spread||function(){for(var t=[],e=0;e=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var b,n,i,o=r(33),a=r(8),L=r(3),s=r(9),g=r(8),M=r(25),c=(l.create=function(t,e){return void 0===e&&(e={}),new l(t,e.handler||{},e.fallback||{},e.items||{},e.tags||{},e.options||{},e.nodes||{},e.preprocessors||[],e.postprocessors||[],[e.init,e.priority],[e.config,e.configPriority])},l.empty=function(){return l.create("empty")},l.extension=function(){return new s.MacroMap(a.ExtensionMaps.NEW_MACRO,{},{}),new s.DelimiterMap(a.ExtensionMaps.NEW_DELIMITER,o.default.delimiter,{}),new s.CommandMap(a.ExtensionMaps.NEW_COMMAND,{},{}),new s.EnvironmentMap(a.ExtensionMaps.NEW_ENVIRONMENT,o.default.environment,{},{}),l.create("extension",{handler:{character:[],delimiter:[a.ExtensionMaps.NEW_DELIMITER],macro:[a.ExtensionMaps.NEW_DELIMITER,a.ExtensionMaps.NEW_COMMAND,a.ExtensionMaps.NEW_MACRO],environment:[a.ExtensionMaps.NEW_ENVIRONMENT]}})},l.prototype.init=function(t){this.initMethod.execute(t)},l.prototype.config=function(t,e){var r,n,i,o;this.configMethod.execute(t,e);try{for(var a=I(this.preprocessors),s=a.next();!s.done;s=a.next()){var c=s.value;"function"==typeof c?e.preFilters.add(c):e.preFilters.add(c[0],c[1])}}catch(t){r={error:t}}finally{try{s&&!s.done&&(n=a.return)&&n.call(a)}finally{if(r)throw r.error}}try{for(var l=I(this.postprocessors),u=l.next();!u.done;u=l.next()){var h=u.value;"function"==typeof h?e.postFilters.add(h):e.postFilters.add(h[0],h[1])}}catch(t){i={error:t}}finally{try{u&&!u.done&&(o=l.return)&&o.call(l)}finally{if(i)throw i.error}}},l.prototype.append=function(t){var e,r,n,i,o,a,s,c,l,u,h,f,p=Object.keys(t.handler);try{for(var d=I(p),m=d.next();!m.done;m=d.next()){var y=m.value;try{for(var v=(n=void 0,I(t.handler[y])),b=v.next();!b.done;b=v.next()){var g=b.value;this.handler[y].unshift(g)}}catch(t){n={error:t}}finally{try{b&&!b.done&&(i=v.return)&&i.call(v)}finally{if(n)throw n.error}}}}catch(t){e={error:t}}finally{try{m&&!m.done&&(r=d.return)&&r.call(d)}finally{if(e)throw e.error}}Object.assign(this.fallback,t.fallback),Object.assign(this.items,t.items),Object.assign(this.tags,t.tags),L.defaultOptions(this.options,t.options),Object.assign(this.nodes,t.nodes);try{for(var M=I(t.preprocessors),O=M.next();!O.done;O=M.next()){var x=O.value;this.preprocessors.push(x)}}catch(t){o={error:t}}finally{try{O&&!O.done&&(a=M.return)&&a.call(M)}finally{if(o)throw o.error}}try{for(var S=I(t.postprocessors),E=S.next();!E.done;E=S.next()){var C=E.value;this.postprocessors.push(C)}}catch(t){s={error:t}}finally{try{E&&!E.done&&(c=S.return)&&c.call(S)}finally{if(s)throw s.error}}try{for(var _=I(t.initMethod),T=_.next();!T.done;T=_.next()){var w=T.value;this.initMethod.add(w.item,w.priority)}}catch(t){l={error:t}}finally{try{T&&!T.done&&(u=_.return)&&u.call(_)}finally{if(l)throw l.error}}try{for(var A=I(t.configMethod),k=A.next();!k.done;k=A.next())w=k.value,this.configMethod.add(w.item,w.priority)}catch(t){h={error:t}}finally{try{k&&!k.done&&(f=A.return)&&f.call(A)}finally{if(h)throw h.error}}},l.prototype.register=function(t,e,r){var n,i,o,a,s,c;void 0===r&&(r={}),this.append(t),t.init(this);var l=e.parseOptions;l.handlers=new g.SubHandlers(this),l.nodeFactory.setCreators(t.nodes);try{for(var u=I(Object.keys(t.items)),h=u.next();!h.done;h=u.next()){var f=h.value;l.itemFactory.setNodeClass(f,t.items[f])}}catch(t){n={error:t}}finally{try{h&&!h.done&&(i=u.return)&&i.call(u)}finally{if(n)throw n.error}}L.defaultOptions(l.options,t.options),L.userOptions(l.options,r),t.config(this,e);try{for(var p=I(t.preprocessors),d=p.next();!d.done;d=p.next()){var m=d.value;Array.isArray(m)?e.preFilters.add(m[0],m[1]):e.preFilters.add(m)}}catch(t){o={error:t}}finally{try{d&&!d.done&&(a=p.return)&&a.call(p)}finally{if(o)throw o.error}}try{for(var y=I(t.postprocessors),v=y.next();!v.done;v=y.next()){var b=v.value;Array.isArray(b)?e.postFilters.add(b[0],b[1]):e.postFilters.add(b)}}catch(t){s={error:t}}finally{try{v&&!v.done&&(c=y.return)&&c.call(y)}finally{if(s)throw s.error}}},l);function l(t,e,r,n,i,o,a,s,c,l,u){void 0===e&&(e={}),void 0===r&&(r={}),void 0===n&&(n={}),void 0===i&&(i={}),void 0===o&&(o={}),void 0===a&&(a={}),void 0===s&&(s=[]),void 0===c&&(c=[]);var h=v(l,2),f=h[0],p=h[1],d=v(u,2),m=d[0],y=d[1];this.name=t,this.handler=e,this.fallback=r,this.items=n,this.tags=i,this.options=o,this.nodes=a,this.preprocessors=s,this.postprocessors=c,this.initMethod=new M.FunctionList,this.configMethod=new M.FunctionList,f&&this.initMethod.add(f,p||0),m&&this.configMethod.add(m,y||p||0),this.handler=Object.assign({character:[],delimiter:[],macro:[],environment:[]},e),b.set(t,this)}e.Configuration=c,n=b=e.ConfigurationHandler||(e.ConfigurationHandler={}),i=new Map,n.set=function(t,e){i.set(t,e)},n.get=function(t){return i.get(t)},n.keys=function(){return i.keys()}},function(t,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var i=e(69),o=e(103);n.options={loadMissingEntities:!0},n.entities={ApplyFunction:"\u2061",Backslash:"\u2216",Because:"\u2235",Breve:"\u02d8",Cap:"\u22d2",CenterDot:"\xb7",CircleDot:"\u2299",CircleMinus:"\u2296",CirclePlus:"\u2295",CircleTimes:"\u2297",Congruent:"\u2261",ContourIntegral:"\u222e",Coproduct:"\u2210",Cross:"\u2a2f",Cup:"\u22d3",CupCap:"\u224d",Dagger:"\u2021",Del:"\u2207",Delta:"\u0394",Diamond:"\u22c4",DifferentialD:"\u2146",DotEqual:"\u2250",DoubleDot:"\xa8",DoubleRightTee:"\u22a8",DoubleVerticalBar:"\u2225",DownArrow:"\u2193",DownLeftVector:"\u21bd",DownRightVector:"\u21c1",DownTee:"\u22a4",Downarrow:"\u21d3",Element:"\u2208",EqualTilde:"\u2242",Equilibrium:"\u21cc",Exists:"\u2203",ExponentialE:"\u2147",FilledVerySmallSquare:"\u25aa",ForAll:"\u2200",Gamma:"\u0393",Gg:"\u22d9",GreaterEqual:"\u2265",GreaterEqualLess:"\u22db",GreaterFullEqual:"\u2267",GreaterLess:"\u2277",GreaterSlantEqual:"\u2a7e",GreaterTilde:"\u2273",Hacek:"\u02c7",Hat:"^",HumpDownHump:"\u224e",HumpEqual:"\u224f",Im:"\u2111",ImaginaryI:"\u2148",Integral:"\u222b",Intersection:"\u22c2",InvisibleComma:"\u2063",InvisibleTimes:"\u2062",Lambda:"\u039b",Larr:"\u219e",LeftAngleBracket:"\u27e8",LeftArrow:"\u2190",LeftArrowRightArrow:"\u21c6",LeftCeiling:"\u2308",LeftDownVector:"\u21c3",LeftFloor:"\u230a",LeftRightArrow:"\u2194",LeftTee:"\u22a3",LeftTriangle:"\u22b2",LeftTriangleEqual:"\u22b4",LeftUpVector:"\u21bf",LeftVector:"\u21bc",Leftarrow:"\u21d0",Leftrightarrow:"\u21d4",LessEqualGreater:"\u22da",LessFullEqual:"\u2266",LessGreater:"\u2276",LessSlantEqual:"\u2a7d",LessTilde:"\u2272",Ll:"\u22d8",Lleftarrow:"\u21da",LongLeftArrow:"\u27f5",LongLeftRightArrow:"\u27f7",LongRightArrow:"\u27f6",Longleftarrow:"\u27f8",Longleftrightarrow:"\u27fa",Longrightarrow:"\u27f9",Lsh:"\u21b0",MinusPlus:"\u2213",NestedGreaterGreater:"\u226b",NestedLessLess:"\u226a",NotDoubleVerticalBar:"\u2226",NotElement:"\u2209",NotEqual:"\u2260",NotExists:"\u2204",NotGreater:"\u226f",NotGreaterEqual:"\u2271",NotLeftTriangle:"\u22ea",NotLeftTriangleEqual:"\u22ec",NotLess:"\u226e",NotLessEqual:"\u2270",NotPrecedes:"\u2280",NotPrecedesSlantEqual:"\u22e0",NotRightTriangle:"\u22eb",NotRightTriangleEqual:"\u22ed",NotSubsetEqual:"\u2288",NotSucceeds:"\u2281",NotSucceedsSlantEqual:"\u22e1",NotSupersetEqual:"\u2289",NotTilde:"\u2241",NotVerticalBar:"\u2224",Omega:"\u03a9",OverBar:"\u203e",OverBrace:"\u23de",PartialD:"\u2202",Phi:"\u03a6",Pi:"\u03a0",PlusMinus:"\xb1",Precedes:"\u227a",PrecedesEqual:"\u2aaf",PrecedesSlantEqual:"\u227c",PrecedesTilde:"\u227e",Product:"\u220f",Proportional:"\u221d",Psi:"\u03a8",Rarr:"\u21a0",Re:"\u211c",ReverseEquilibrium:"\u21cb",RightAngleBracket:"\u27e9",RightArrow:"\u2192",RightArrowLeftArrow:"\u21c4",RightCeiling:"\u2309",RightDownVector:"\u21c2",RightFloor:"\u230b",RightTee:"\u22a2",RightTeeArrow:"\u21a6",RightTriangle:"\u22b3",RightTriangleEqual:"\u22b5",RightUpVector:"\u21be",RightVector:"\u21c0",Rightarrow:"\u21d2",Rrightarrow:"\u21db",Rsh:"\u21b1",Sigma:"\u03a3",SmallCircle:"\u2218",Sqrt:"\u221a",Square:"\u25a1",SquareIntersection:"\u2293",SquareSubset:"\u228f",SquareSubsetEqual:"\u2291",SquareSuperset:"\u2290",SquareSupersetEqual:"\u2292",SquareUnion:"\u2294",Star:"\u22c6",Subset:"\u22d0",SubsetEqual:"\u2286",Succeeds:"\u227b",SucceedsEqual:"\u2ab0",SucceedsSlantEqual:"\u227d",SucceedsTilde:"\u227f",SuchThat:"\u220b",Sum:"\u2211",Superset:"\u2283",SupersetEqual:"\u2287",Supset:"\u22d1",Therefore:"\u2234",Theta:"\u0398",Tilde:"\u223c",TildeEqual:"\u2243",TildeFullEqual:"\u2245",TildeTilde:"\u2248",UnderBar:"_",UnderBrace:"\u23df",Union:"\u22c3",UnionPlus:"\u228e",UpArrow:"\u2191",UpDownArrow:"\u2195",UpTee:"\u22a5",Uparrow:"\u21d1",Updownarrow:"\u21d5",Upsilon:"\u03a5",Vdash:"\u22a9",Vee:"\u22c1",VerticalBar:"\u2223",VerticalTilde:"\u2240",Vvdash:"\u22aa",Wedge:"\u22c0",Xi:"\u039e",amp:"&",acute:"\xb4",aleph:"\u2135",alpha:"\u03b1",amalg:"\u2a3f",and:"\u2227",ang:"\u2220",angmsd:"\u2221",angsph:"\u2222",ape:"\u224a",backprime:"\u2035",backsim:"\u223d",backsimeq:"\u22cd",beta:"\u03b2",beth:"\u2136",between:"\u226c",bigcirc:"\u25ef",bigodot:"\u2a00",bigoplus:"\u2a01",bigotimes:"\u2a02",bigsqcup:"\u2a06",bigstar:"\u2605",bigtriangledown:"\u25bd",bigtriangleup:"\u25b3",biguplus:"\u2a04",blacklozenge:"\u29eb",blacktriangle:"\u25b4",blacktriangledown:"\u25be",blacktriangleleft:"\u25c2",bowtie:"\u22c8",boxdl:"\u2510",boxdr:"\u250c",boxminus:"\u229f",boxplus:"\u229e",boxtimes:"\u22a0",boxul:"\u2518",boxur:"\u2514",bsol:"\\",bull:"\u2022",cap:"\u2229",check:"\u2713",chi:"\u03c7",circ:"\u02c6",circeq:"\u2257",circlearrowleft:"\u21ba",circlearrowright:"\u21bb",circledR:"\xae",circledS:"\u24c8",circledast:"\u229b",circledcirc:"\u229a",circleddash:"\u229d",clubs:"\u2663",colon:":",comp:"\u2201",ctdot:"\u22ef",cuepr:"\u22de",cuesc:"\u22df",cularr:"\u21b6",cup:"\u222a",curarr:"\u21b7",curlyvee:"\u22ce",curlywedge:"\u22cf",dagger:"\u2020",daleth:"\u2138",ddarr:"\u21ca",deg:"\xb0",delta:"\u03b4",digamma:"\u03dd",div:"\xf7",divideontimes:"\u22c7",dot:"\u02d9",doteqdot:"\u2251",dotplus:"\u2214",dotsquare:"\u22a1",dtdot:"\u22f1",ecir:"\u2256",efDot:"\u2252",egs:"\u2a96",ell:"\u2113",els:"\u2a95",empty:"\u2205",epsi:"\u03b5",epsiv:"\u03f5",erDot:"\u2253",eta:"\u03b7",eth:"\xf0",flat:"\u266d",fork:"\u22d4",frown:"\u2322",gEl:"\u2a8c",gamma:"\u03b3",gap:"\u2a86",gimel:"\u2137",gnE:"\u2269",gnap:"\u2a8a",gne:"\u2a88",gnsim:"\u22e7",gt:">",gtdot:"\u22d7",harrw:"\u21ad",hbar:"\u210f",hellip:"\u2026",hookleftarrow:"\u21a9",hookrightarrow:"\u21aa",imath:"\u0131",infin:"\u221e",intcal:"\u22ba",iota:"\u03b9",jmath:"\u0237",kappa:"\u03ba",kappav:"\u03f0",lEg:"\u2a8b",lambda:"\u03bb",lap:"\u2a85",larrlp:"\u21ab",larrtl:"\u21a2",lbrace:"{",lbrack:"[",le:"\u2264",leftleftarrows:"\u21c7",leftthreetimes:"\u22cb",lessdot:"\u22d6",lmoust:"\u23b0",lnE:"\u2268",lnap:"\u2a89",lne:"\u2a87",lnsim:"\u22e6",longmapsto:"\u27fc",looparrowright:"\u21ac",lowast:"\u2217",loz:"\u25ca",lt:"<",ltimes:"\u22c9",ltri:"\u25c3",macr:"\xaf",malt:"\u2720",mho:"\u2127",mu:"\u03bc",multimap:"\u22b8",nLeftarrow:"\u21cd",nLeftrightarrow:"\u21ce",nRightarrow:"\u21cf",nVDash:"\u22af",nVdash:"\u22ae",natur:"\u266e",nearr:"\u2197",nharr:"\u21ae",nlarr:"\u219a",not:"\xac",nrarr:"\u219b",nu:"\u03bd",nvDash:"\u22ad",nvdash:"\u22ac",nwarr:"\u2196",omega:"\u03c9",omicron:"\u03bf",or:"\u2228",osol:"\u2298",period:".",phi:"\u03c6",phiv:"\u03d5",pi:"\u03c0",piv:"\u03d6",prap:"\u2ab7",precnapprox:"\u2ab9",precneqq:"\u2ab5",precnsim:"\u22e8",prime:"\u2032",psi:"\u03c8",quot:'"',rarrtl:"\u21a3",rbrace:"}",rbrack:"]",rho:"\u03c1",rhov:"\u03f1",rightrightarrows:"\u21c9",rightthreetimes:"\u22cc",ring:"\u02da",rmoust:"\u23b1",rtimes:"\u22ca",rtri:"\u25b9",scap:"\u2ab8",scnE:"\u2ab6",scnap:"\u2aba",scnsim:"\u22e9",sdot:"\u22c5",searr:"\u2198",sect:"\xa7",sharp:"\u266f",sigma:"\u03c3",sigmav:"\u03c2",simne:"\u2246",smile:"\u2323",spades:"\u2660",sub:"\u2282",subE:"\u2ac5",subnE:"\u2acb",subne:"\u228a",supE:"\u2ac6",supnE:"\u2acc",supne:"\u228b",swarr:"\u2199",tau:"\u03c4",theta:"\u03b8",thetav:"\u03d1",tilde:"\u02dc",times:"\xd7",triangle:"\u25b5",triangleq:"\u225c",upsi:"\u03c5",upuparrows:"\u21c8",veebar:"\u22bb",vellip:"\u22ee",weierp:"\u2118",xi:"\u03be",yen:"\xa5",zeta:"\u03b6",zigrarr:"\u21dd"};var a={};function r(t,e){if("#"===e.charAt(0))return s(e.slice(1));if(n.entities[e])return n.entities[e];if(n.options.loadMissingEntities){var r=e.match(/^[a-zA-Z](fr|scr|opf)$/)?RegExp.$1:e.charAt(0).toLowerCase();a[r]||(a[r]=!0,i.retryAfter(o.asyncLoad("./util/entities/"+r+".js")))}return t}function s(t){var e="x"===t.charAt(0)?parseInt(t.slice(1),16):parseInt(t);if(e<65536)return String.fromCharCode(e);var r=55296+((e-=65536)>>10),n=56320+(1023&e);return String.fromCharCode(r,n)}n.add=function(t,e){Object.assign(n.entities,t),a[e]=!0},n.remove=function(t){delete n.entities[t]},n.translate=function(t){return t.replace(/&([a-z][a-z0-9]*|#(?:[0-9]+|x[0-9a-f]+));/gi,r)},n.numeric=s},function(t,o,e){"use strict";Object.defineProperty(o,"__esModule",{value:!0}),o.protoItem=function(t,e,r,n,i,o,a){return void 0===a&&(a=null),{open:t,math:e,close:r,n:n,start:{n:i},end:{n:o},display:a}};var r=(n.prototype.render=function(t){t.renderActions.renderMath(this,t)},n.prototype.rerender=function(t,e){void 0===e&&(e=o.STATE.RERENDER),this.state()>=e&&this.state(e-1),t.renderActions.renderMath(this,t,e)},n.prototype.convert=function(t,e){void 0===e&&(e=o.STATE.LAST),t.renderActions.renderConvert(this,t,e)},n.prototype.compile=function(t){this.state()=o.STATE.INSERTED&&this.removeFromDocument(e),t=o.STATE.TYPESET&&(this.bbox={},this.outputData={}),t=o.STATE.COMPILED&&(this.inputData={}),this._state=t),this._state},n.prototype.reset=function(t){void 0===t&&(t=!1),this.state(o.STATE.UNPROCESSED)},n);function n(t,e,r,n,i){void 0===r&&(r=!0),void 0===n&&(n={i:0,n:0,delim:""}),void 0===i&&(i={i:0,n:0,delim:""}),this.root=null,this.typesetRoot=null,this._state=o.STATE.UNPROCESSED,this.metrics={},this.bbox={},this.inputData={},this.outputData={},this.math=t,this.inputJax=e,this.display=r,this.start=n,this.end=i,this.root=null,this.typesetRoot=null,this.metrics={},this.bbox={},this.inputData={},this.outputData={}}o.AbstractMathItem=r,o.STATE={UNPROCESSED:0,FINDMATH:10,COMPILED:20,CONVERT:100,METRICS:110,RERENDER:125,TYPESET:150,INSERTED:200,RESET:500,LAST:1e4},o.newState=function(t,e){if(t in o.STATE)throw Error("State "+t+" already exists");o.STATE[t]=e}},function(t,s,e){"use strict";Object.defineProperty(s,"__esModule",{value:!0}),s.BIGDIMEN=1e6,s.UNITS={px:1,pt:96/72,pc:8,in:96,cm:96/2.54,mm:96/25.4},s.RELUNITS={em:1,ex:.431,mu:1/18},s.MATHSPACE={veryverythinmathspace:1/18,verythinmathspace:2/18,thinmathspace:3/18,mediummathspace:4/18,thickmathspace:5/18,verythickmathspace:6/18,veryverythickmathspace:7/18,negativeveryverythinmathspace:-1/18,negativeverythinmathspace:-2/18,negativethinmathspace:-3/18,negativemediummathspace:-4/18,negativethickmathspace:-5/18,negativeverythickmathspace:-6/18,negativeveryverythickmathspace:-7/18,thin:.04,medium:.06,thick:.1,normal:1,big:2,small:1/Math.sqrt(2),infinity:s.BIGDIMEN},s.length2em=function(t,e,r,n){if(void 0===e&&(e=0),void 0===r&&(r=1),void 0===n&&(n=16),"string"!=typeof t&&(t=String(t)),""===t||null==t)return e;if(s.MATHSPACE[t])return s.MATHSPACE[t];var i=t.match(/^\s*([-+]?(?:\.\d+|\d+(?:\.\d*)?))?(pt|em|ex|mu|px|pc|in|mm|cm|%)?/);if(!i)return e;var o=parseFloat(i[1]||"1"),a=i[2];return s.UNITS.hasOwnProperty(a)?o*s.UNITS[a]/n/r:s.RELUNITS.hasOwnProperty(a)?o*s.RELUNITS[a]:"%"===a?o/100*e:o*e},s.percent=function(t){return(100*t).toFixed(1).replace(/\.?0+$/,"")+"%"},s.em=function(t){return Math.abs(t)<.001?"0":t.toFixed(3).replace(/\.?0+$/,"")+"em"},s.emRounded=function(t,e){return void 0===e&&(e=16),t=(Math.round(t*e)+.05)/e,Math.abs(t)<.001?"0em":t.toFixed(3).replace(/\.?0+$/,"")+"em"},s.px=function(t,e,r){return void 0===e&&(e=-s.BIGDIMEN),void 0===r&&(r=16),t*=r,e&&tthis.w&&(this.w=i),o>this.h&&(this.h=o),a>this.d&&(this.d=a)},o.prototype.append=function(t){var e=t.rscale;this.w+=e*(t.w+t.L+t.R),e*t.h>this.h&&(this.h=e*t.h),e*t.d>this.d&&(this.d=e*t.d)},o.prototype.updateFrom=function(t){this.h=t.h,this.d=t.d,this.w=t.w,t.pwidth&&(this.pwidth=t.pwidth)},o.fullWidth="100%",o);function o(t){void 0===t&&(t={w:0,h:-n.BIGDIMEN,d:-n.BIGDIMEN}),this.w=t.w||0,this.h="h"in t?t.h:-n.BIGDIMEN,this.d="d"in t?t.d:-n.BIGDIMEN,this.L=this.R=this.ic=this.sk=0,this.scale=this.rscale=1,this.pwidth=""}e.BBox=i},function(t,h,o){"use strict";(function(r){var l=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(h,"__esModule",{value:!0});var t,e,n=o(5),u=o(18),i=o(18);h.Package=i.Package,h.PackageError=i.PackageError,(e=t=h.Loader||(h.Loader={})).ready=function(){for(var e,t,r=[],n=0;n=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var a,p=r(17),s=(a=Error,i(c,a),c);function c(t,e){var r=a.call(this,t)||this;return r.package=e,r}e.PackageError=s;var l=(d.resolvePath=function(t,e){void 0===e&&(e=!0);var r,n=p.CONFIG.source[t]||t;for(n.match(/^(?:[a-z]+:\/)?\/|\[/)||(n="[mathjax]/"+n.replace(/^\.\//,"")),e&&!n.match(/\.[^\/]+$/)&&(n+=".js");(r=n.match(/^\[([^\]]*)\]/))&&p.CONFIG.paths.hasOwnProperty(r[1]);)n=p.CONFIG.paths[r[1]]+n.substr(r[0].length);return n},Object.defineProperty(d.prototype,"canLoad",{get:function(){return 0===this.dependencyCount&&!this.noLoad&&!this.isLoading&&!this.hasFailed},enumerable:!0,configurable:!0}),d.prototype.makeDependencies=function(){var e,t,r=[],n=d.packages,i=this.noLoad,o=this.name,a=[];p.CONFIG.dependencies.hasOwnProperty(o)?a.push.apply(a,h(p.CONFIG.dependencies[o])):"core"!==o&&a.push("core");try{for(var s=f(a),c=s.next();!c.done;c=s.next()){var l=c.value,u=n.get(l)||new d(l,i);this.dependencies.indexOf(u)<0&&(u.addDependent(this,i),this.dependencies.push(u),u.isLoaded||(this.dependencyCount++,r.push(u.promise)))}}catch(t){e={error:t}}finally{try{c&&!c.done&&(t=s.return)&&t.call(s)}finally{if(e)throw e.error}}return r},d.prototype.makePromise=function(t){var r=this,e=new Promise(function(t,e){r.resolve=t,r.reject=e}),n=p.CONFIG[this.name]||{};return n.ready&&(e=e.then(function(t){return n.ready(r.name)})),t.length&&(t.push(e),e=Promise.all(t).then(function(t){return t.join(", ")})),n.failed&&e.catch(function(t){return n.failed(new s(t,r.name))}),e},d.prototype.load=function(){if(!this.isLoaded&&!this.isLoading&&!this.noLoad){this.isLoading=!0;var t=d.resolvePath(this.name);p.CONFIG.require?this.loadCustom(t):this.loadScript(t)}},d.prototype.loadCustom=function(t){var e=this;try{var r=p.CONFIG.require(t);r instanceof Promise?r.then(function(){return e.checkLoad()}).catch(function(){return e.failed("Can't load \""+t+'"')}):this.checkLoad()}catch(t){this.failed(t.message)}},d.prototype.loadScript=function(e){var r=this,t=document.createElement("script");t.src=e,t.charset="UTF-8",t.onload=function(t){return r.checkLoad()},t.onerror=function(t){return r.failed("Can't load \""+e+'"')},document.head.appendChild(t)},d.prototype.loaded=function(){var e,t,r,n;this.isLoaded=!0,this.isLoading=!1;try{for(var i=f(this.dependents),o=i.next();!o.done;o=i.next())o.value.requirementSatisfied()}catch(t){e={error:t}}finally{try{o&&!o.done&&(t=i.return)&&t.call(i)}finally{if(e)throw e.error}}try{for(var a=f(this.provided),s=a.next();!s.done;s=a.next())s.value.loaded()}catch(t){r={error:t}}finally{try{s&&!s.done&&(n=a.return)&&n.call(a)}finally{if(r)throw r.error}}this.resolve(this.name)},d.prototype.failed=function(t){this.hasFailed=!0,this.isLoading=!1,this.reject(new s(t,this.name))},d.prototype.checkLoad=function(){var e=this;((p.CONFIG[this.name]||{}).checkReady||function(){return Promise.resolve()})().then(function(){return e.loaded()}).catch(function(t){return e.failed(t)})},d.prototype.requirementSatisfied=function(){this.dependencyCount&&(this.dependencyCount--,this.canLoad&&this.load())},d.prototype.provides=function(t){var e,r;void 0===t&&(t=[]);try{for(var n=f(t),i=n.next();!i.done;i=n.next()){var o=i.value,a=d.packages.get(o);a||(p.CONFIG.dependencies[o]||(p.CONFIG.dependencies[o]=[]),p.CONFIG.dependencies[o].push(o),(a=new d(o,!0)).isLoading=!0),this.provided.push(a)}}catch(t){e={error:t}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(e)throw e.error}}},d.prototype.addDependent=function(t,e){this.dependents.push(t),e||this.checkNoLoad()},d.prototype.checkNoLoad=function(){var e,t;if(this.noLoad){this.noLoad=!1;try{for(var r=f(this.dependencies),n=r.next();!n.done;n=r.next())n.value.checkNoLoad()}catch(t){e={error:t}}finally{try{n&&!n.done&&(t=r.return)&&t.call(r)}finally{if(e)throw e.error}}}},d.loadAll=function(){var e,t;try{for(var r=f(this.packages.values()),n=r.next();!n.done;n=r.next()){var i=n.value;i.canLoad&&i.load()}}catch(t){e={error:t}}finally{try{n&&!n.done&&(t=r.return)&&t.call(r)}finally{if(e)throw e.error}}},d.packages=new Map,d);function d(t,e){void 0===e&&(e=!1),this.isLoaded=!1,this.isLoading=!1,this.hasFailed=!1,this.dependents=[],this.dependencies=[],this.dependencyCount=0,this.provided=[],this.name=t,this.noLoad=e,d.packages.set(t,this),this.promise=this.makePromise(this.makeDependencies())}e.Package=l},function(t,r,e){"use strict";var c=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(r,"__esModule",{value:!0}),r.INHERIT="_inherit_";var n=(i.prototype.set=function(t,e){this.attributes[t]=e},i.prototype.setList=function(t){Object.assign(this.attributes,t)},i.prototype.get=function(t){var e=this.attributes[t];return e===r.INHERIT&&(e=this.global[t]),e},i.prototype.getExplicit=function(t){if(this.attributes.hasOwnProperty(t))return this.attributes[t]},i.prototype.getList=function(){for(var e,t,r=[],n=0;n=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},s=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0}),e.V=1,e.H=2,e.NOSTRETCH={dir:0};var i=(o.charOptions=function(t,e){var r=t[e];return 3===r.length&&(r[3]={}),r[3]},o.prototype.createVariant=function(t,e,r){void 0===e&&(e=null),void 0===r&&(r=null);var n={linked:[],chars:e?Object.create(this.variant[e].chars):{}};r&&this.variant[r]&&(Object.assign(n.chars,this.variant[r].chars),this.variant[r].linked.push(n.chars),n.chars=Object.create(n.chars)),this.variant[t]=n},o.prototype.createVariants=function(t){var e,r;try{for(var n=c(t),i=n.next();!i.done;i=n.next()){var o=i.value;this.createVariant(o[0],o[1],o[2])}}catch(t){e={error:t}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(e)throw e.error}}},o.prototype.defineChars=function(t,e){var r,n,i=this.variant[t];Object.assign(i.chars,e);try{for(var o=c(i.linked),a=o.next();!a.done;a=o.next()){var s=a.value;Object.assign(s,e)}}catch(t){r={error:t}}finally{try{a&&!a.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}},o.prototype.defineDelimiters=function(t){Object.assign(this.delimiters,t)},o.prototype.defineRemap=function(t,e){this.remapChars.hasOwnProperty(t)||(this.remapChars[t]={}),Object.assign(this.remapChars[t],e)},o.prototype.getDelimiter=function(t){return this.delimiters[t]},o.prototype.getSizeVariant=function(t,e){return this.delimiters[t].variants&&(e=this.delimiters[t].variants[e]),this.sizeVariants[e]},o.prototype.getChar=function(t,e){return this.variant[t].chars[e]},o.prototype.getVariant=function(t){return this.variant[t]},o.prototype.getCssFont=function(t){return this.cssFontMap[t]||["serif",!1,!1]},o.prototype.getRemappedChar=function(t,e){return(this.remapChars[t]||{})[e]},o.OPTIONS={},o.defaultVariants=[["normal"],["bold","normal"],["italic","normal"],["bold-italic","italic","bold"],["double-struck","bold"],["fraktur","normal"],["bold-fraktur","bold","fraktur"],["script","normal"],["bold-script","bold","script"],["sans-serif","normal"],["bold-sans-serif","bold","sans-serif"],["sans-serif-italic","italic","sans-serif"],["bold-sans-serif-italic","bold-italic","sans-serif"],["monospace","normal"]],o.defaultCssFonts={normal:["serif",!1,!1],bold:["serif",!1,!0],italic:["serif",!0,!1],"bold-italic":["serif",!0,!0],"double-struck":["serif",!1,!0],fraktur:["serif",!1,!1],"bold-fraktur":["serif",!1,!0],script:["cursive",!1,!1],"bold-script":["cursive",!1,!0],"sans-serif":["sans-serif",!1,!1],"bold-sans-serif":["sans-serif",!1,!0],"sans-serif-italic":["sans-serif",!0,!1],"bold-sans-serif-italic":["sans-serif",!0,!0],monospace:["monospace",!1,!1]},o.defaultAccentMap={768:"\u02cb",769:"\u02ca",770:"\u02c6",771:"\u02dc",772:"\u02c9",774:"\u02d8",775:"\u02d9",776:"\xa8",778:"\u02da",780:"\u02c7",8594:"\u20d7",8242:"'",8243:"''",8244:"'''",8245:"`",8246:"``",8247:"```",8279:"''''",8400:"\u21bc",8401:"\u21c0",8406:"\u2190",8417:"\u2194",8432:"*",8411:"...",8412:"....",8428:"\u21c1",8429:"\u21bd",8430:"\u2190",8431:"\u2192"},o.defaultMoMap={45:"\u2212"},o.defaultMnMap={45:"\u2212"},o.defaultParams={x_height:.442,quad:1,num1:.676,num2:.394,num3:.444,denom1:.686,denom2:.345,sup1:.413,sup2:.363,sup3:.289,sub1:.15,sub2:.247,sup_drop:.386,sub_drop:.05,delim1:2.39,delim2:1,axis_height:.25,rule_thickness:.06,big_op_spacing1:.111,big_op_spacing2:.167,big_op_spacing3:.2,big_op_spacing4:.6,big_op_spacing5:.1,surd_height:.075,scriptspace:.05,nulldelimiterspace:.12,delimiterfactor:901,delimitershortfall:.3,min_rule_thickness:1.25},o.defaultDelimiters={},o.defaultChars={},o.defaultSizeVariants=[],o);function o(){var e,t;this.variant={},this.delimiters={},this.cssFontMap={},this.remapChars={};var r=this.constructor;this.params=a({},r.defaultParams),this.sizeVariants=s(r.defaultSizeVariants),this.cssFontMap=a({},r.defaultCssFonts),this.createVariants(r.defaultVariants),this.defineDelimiters(r.defaultDelimiters);try{for(var n=c(Object.keys(r.defaultChars)),i=n.next();!i.done;i=n.next()){var o=i.value;this.defineChars(o,r.defaultChars[o])}}catch(t){e={error:t}}finally{try{i&&!i.done&&(t=n.return)&&t.call(n)}finally{if(e)throw e.error}}this.defineRemap("accent",r.defaultAccentMap),this.defineRemap("mo",r.defaultMoMap),this.defineRemap("mn",r.defaultMnMap)}e.FontData=i},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=(i.prototype[Symbol.iterator]=function(){var t=0,e=this.items;return{next:function(){return{value:e[t++],done:t>e.length}}}},i.prototype.add=function(t,e){void 0===e&&(e=i.DEFAULTPRIORITY);for(var r=this.items.length;0<=--r&&e=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var M,a=r(0),s=r(86),c=(M=a.AbstractMmlTokenNode,i(l,M),Object.defineProperty(l.prototype,"kind",{get:function(){return"mo"},enumerable:!0,configurable:!0}),Object.defineProperty(l.prototype,"isEmbellished",{get:function(){return!0},enumerable:!0,configurable:!0}),Object.defineProperty(l.prototype,"hasNewLine",{get:function(){return"newline"===this.attributes.get("linebreak")},enumerable:!0,configurable:!0}),l.prototype.coreParent=function(){for(var t=this,e=this.factory.getNodeClass("math");t&&t.isEmbellished&&t.coreMO()===this&&!(t instanceof e);)t=t.Parent;return t},l.prototype.coreText=function(t){if(!t)return"";if(t.isEmbellished)return t.coreMO().getText();for(;((t.isKind("mrow")||t.isKind("TeXAtom")||t.isKind("mstyle")||t.isKind("mphantom"))&&1===t.childNodes.length||t.isKind("munderover"))&&t.childNodes[0];)t=t.childNodes[0];return t.isToken?t.getText():""},l.prototype.hasSpacingAttributes=function(){return this.attributes.isSet("lspace")||this.attributes.isSet("rspace")},Object.defineProperty(l.prototype,"isAccent",{get:function(){var t=!1,e=this.coreParent();if(e){var r=e.isKind("mover")?e.childNodes[e.over].coreMO()?"accent":"":e.isKind("munder")?e.childNodes[e.under].coreMO()?"accentunder":"":e.isKind("munderover")?this===e.childNodes[e.over].coreMO()?"accent":this===e.childNodes[e.under].coreMO()?"accentunder":"":"";r&&(t=void 0!==e.attributes.getExplicit(r)?t:this.attributes.get("accent"))}return t},enumerable:!0,configurable:!0}),l.prototype.setTeXclass=function(t){var e=this.attributes.getList("form","fence"),r=e.form,n=e.fence;return this.attributes.isSet("lspace")||this.attributes.isSet("rspace")?(this.texClass=a.TEXCLASS.NONE,null):(n&&this.texClass===a.TEXCLASS.REL&&("prefix"===r&&(this.texClass=a.TEXCLASS.OPEN),"postfix"===r&&(this.texClass=a.TEXCLASS.CLOSE)),"\u2061"===this.getText()?(t&&(t.texClass=a.TEXCLASS.OP,t.setProperty("fnOP",!0)),this.texClass=this.prevClass=a.TEXCLASS.NONE,t):this.adjustTeXclass(t))},l.prototype.adjustTeXclass=function(t){var e=this.texClass,r=this.prevClass;if(e===a.TEXCLASS.NONE)return t;if(t?(!t.getProperty("autoOp")||e!==a.TEXCLASS.BIN&&e!==a.TEXCLASS.REL||(e=this.texClass=a.TEXCLASS.ORD),r=this.prevClass=t.texClass||a.TEXCLASS.ORD,this.prevLevel=this.attributes.getInherited("scriptlevel")):r=this.prevClass=a.TEXCLASS.NONE,e!==a.TEXCLASS.BIN||r!==a.TEXCLASS.NONE&&r!==a.TEXCLASS.BIN&&r!==a.TEXCLASS.OP&&r!==a.TEXCLASS.REL&&r!==a.TEXCLASS.OPEN&&r!==a.TEXCLASS.PUNCT)if(r!==a.TEXCLASS.BIN||e!==a.TEXCLASS.REL&&e!==a.TEXCLASS.CLOSE&&e!==a.TEXCLASS.PUNCT){if(e===a.TEXCLASS.BIN){for(var n=this,i=this.parent;i&&i.parent&&i.isEmbellished&&(1===i.childNodes.length||!i.isKind("mrow")&&i.core()===n);)i=(n=i).parent;i.childNodes[i.childNodes.length-1]===n&&(this.texClass=a.TEXCLASS.ORD)}}else t.texClass=this.prevClass=a.TEXCLASS.ORD;else this.texClass=a.TEXCLASS.ORD;return this},l.prototype.setInheritedAttributes=function(t,e,r,n){var i,o;void 0===t&&(t={}),void 0===e&&(e=!1),void 0===r&&(r=0),void 0===n&&(n=!1),M.prototype.setInheritedAttributes.call(this,t,e,r,n);var a=this.getText(),s=b(this.handleExplicitForm(this.getForms()),3),c=s[0],l=s[1],u=s[2];this.attributes.setInherited("form",c);var h=this.constructor.OPTABLE,f=h[c][a]||h[l][a]||h[u][a];if(f){void 0===this.getProperty("texClass")&&(this.texClass=f[2]);try{for(var p=g(Object.keys(f[3]||{})),d=p.next();!d.done;d=p.next()){var m=d.value;this.attributes.setInherited(m,f[3][m])}}catch(t){i={error:t}}finally{try{d&&!d.done&&(o=p.return)&&o.call(p)}finally{if(i)throw i.error}}this.lspace=(f[0]+1)/18,this.rspace=(f[1]+1)/18}else{var y=this.getRange(a);if(y){void 0===this.getProperty("texClass")&&(this.texClass=y[2]);var v=this.constructor.MMLSPACING[y[2]];this.lspace=(v[0]+1)/18,this.rspace=(v[1]+1)/18}}},l.prototype.getForms=function(){for(var t=this,e=this.parent,r=this.Parent;r&&r.isEmbellished;)t=e,e=r.parent,r=r.Parent;if(e&&e.isKind("mrow")&&1!==e.nonSpaceLength()){if(e.firstNonSpace()===t)return["prefix","infix","postfix"];if(e.lastNonSpace()===t)return["postfix","infix","prefix"]}return["infix","prefix","postfix"]},l.prototype.handleExplicitForm=function(t){if(this.attributes.isSet("form")){var e=this.attributes.get("form");t=[e].concat(t.filter(function(t){return t!==e}))}return t},l.prototype.getRange=function(t){var e,r;if(!t.match(/^[\uD800-\uDBFF]?.$/))return null;var n=t.charCodeAt(0);2===t.length&&(n=1024*(n-55296)+t.charCodeAt(1)-56320+65536);var i=this.constructor.RANGES;try{for(var o=g(i),a=o.next();!a.done;a=o.next()){var s=a.value;if(s[0]<=n&&n<=s[1])return s;if(n=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var o=r(21),s=function(t,e){void 0===t&&(t="???"),void 0===e&&(e=""),this.tag=t,this.id=e};e.Label=s;var c=function(t,e,r,n,i,o,a,s){void 0===t&&(t=""),void 0===e&&(e=!1),void 0===r&&(r=!1),void 0===n&&(n=null),void 0===i&&(i=""),void 0===o&&(o=""),void 0===a&&(a=!1),void 0===s&&(s=""),this.env=t,this.taggable=e,this.defaultTags=r,this.tag=n,this.tagId=i,this.tagFormat=o,this.noTag=a,this.labelId=s};e.TagInfo=c;var l=(u.prototype.start=function(t,e,r){this.currentTag&&this.stack.push(this.currentTag),this.currentTag=new c(t,e,r)},Object.defineProperty(u.prototype,"env",{get:function(){return this.currentTag.env},enumerable:!0,configurable:!0}),u.prototype.end=function(){this.history.push(this.currentTag),this.currentTag=this.stack.pop()},u.prototype.tag=function(t,e){this.currentTag.tag=t,this.currentTag.tagFormat=e?t:this.formatTag(t),this.currentTag.noTag=!1},u.prototype.notag=function(){this.tag("",!0),this.currentTag.noTag=!0},Object.defineProperty(u.prototype,"noTag",{get:function(){return this.currentTag.noTag},enumerable:!0,configurable:!0}),Object.defineProperty(u.prototype,"label",{get:function(){return this.currentTag.labelId},set:function(t){this.currentTag.labelId=t},enumerable:!0,configurable:!0}),u.prototype.formatUrl=function(t,e){return e+"#"+encodeURIComponent(t)},u.prototype.formatTag=function(t){return"("+t+")"},u.prototype.formatId=function(t){return"mjx-eqn-"+t.replace(/\s/g,"_")},u.prototype.formatNumber=function(t){return t.toString()},u.prototype.autoTag=function(){null==this.currentTag.tag&&(this.counter++,this.tag(this.formatNumber(this.counter),!1))},u.prototype.clearTag=function(){this.label="",this.tag(null,!0),this.currentTag.tagId=""},u.prototype.getTag=function(t){if(void 0===t&&(t=!1),t)return this.autoTag(),this.makeTag();var e=this.currentTag;return e.taggable&&!e.noTag&&(e.defaultTags&&this.autoTag(),e.tag)?this.makeTag():null},u.prototype.resetTag=function(){this.history=[],this.redo=!1,this.refUpdate=!1,this.clearTag()},u.prototype.reset=function(t){void 0===t&&(t=0),this.resetTag(),this.counter=this.allCounter=t,this.allLabels={},this.allIds={}},u.prototype.startEquation=function(t){this.labels={},this.ids={},this.counter=this.allCounter,this.redo=!1;var e=t.inputData.recompile;e&&(this.refUpdate=!0,this.counter=e.counter)},u.prototype.finishEquation=function(t){this.redo&&(t.inputData.recompile={state:t.state(),counter:this.allCounter}),this.refUpdate||(this.allCounter=this.counter),Object.assign(this.allIds,this.ids),Object.assign(this.allLabels,this.labels)},u.prototype.finalize=function(t,e){if(!e.display||this.currentTag.env||null==this.currentTag.tag)return t;var r=this.makeTag();return this.enTag(t,r)},u.prototype.makeId=function(){this.currentTag.tagId=this.formatId(this.configuration.options.useLabelIds&&this.label||this.currentTag.tag)},u.prototype.makeTag=function(){this.makeId(),this.label&&(this.labels[this.label]=new s(this.currentTag.tag,this.currentTag.tagId));var t=new o.default("\\text{"+this.currentTag.tagFormat+"}",{},this.configuration).mml();return this.configuration.nodeFactory.create("node","mtd",[t],{id:this.currentTag.tagId})},u);function u(){this.counter=0,this.allCounter=0,this.configuration=null,this.ids={},this.allIds={},this.labels={},this.allLabels={},this.redo=!1,this.refUpdate=!1,this.currentTag=new c,this.history=[],this.stack=[],this.enTag=function(t,e){var r=this.configuration.nodeFactory,n=r.create("node","mtd",[t]),i=r.create("node","mlabeledtr",[e,n]);return r.create("node","mtable",[i],{side:this.configuration.options.tagSide,minlabelspacing:this.configuration.options.tagIndent,displaystyle:!0})}}e.AbstractTags=l;var h,f=(i(p,h=l),p.prototype.autoTag=function(){},p.prototype.getTag=function(){return this.currentTag.tag?h.prototype.getTag.call(this):null},p);function p(){return null!==h&&h.apply(this,arguments)||this}e.NoTags=f;var d,m,y,v,b=(i(g,d=l),g.prototype.finalize=function(t,e){if(!e.display||this.history.find(function(t){return t.taggable}))return t;var r=this.getTag(!0);return this.enTag(t,r)},g);function g(){return null!==d&&d.apply(this,arguments)||this}e.AllTags=b,m=e.TagsFactory||(e.TagsFactory={}),y=new Map([["none",f],["all",b]]),v="none",m.OPTIONS={tags:v,tagSide:"right",tagIndent:"0.8em",multlineWidth:"85%",useLabelIds:!0,ignoreDuplicateLabels:!1},m.add=function(t,e){y.set(t,e)},m.addTags=function(t){var e,r;try{for(var n=a(Object.keys(t)),i=n.next();!i.done;i=n.next()){var o=i.value;m.add(o,t[o])}}catch(t){e={error:t}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(e)throw e.error}}},m.create=function(t){return new(y.get(t)||this.defaultTags)},m.setDefault=function(t){v=t},m.getDefault=function(){return m.create(v)}},function($K,_K){var aL;aL=function(){return this}();try{aL=aL||Function("return this")()||eval("this")}catch(t){"object"==typeof window&&(aL=window)}$K.exports=aL},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(3),i=r(25),o=(Object.defineProperty(a.prototype,"name",{get:function(){return this.constructor.NAME},enumerable:!0,configurable:!0}),a.prototype.setAdaptor=function(t){this.adaptor=t},a.prototype.setMmlFactory=function(t){this.mmlFactory=t},a.prototype.initialize=function(){},Object.defineProperty(a.prototype,"processStrings",{get:function(){return!0},enumerable:!0,configurable:!0}),a.prototype.findMath=function(t,e){return[]},a.prototype.executeFilters=function(t,e,r,n){var i={math:e,document:r,data:n};return t.execute(i),i.data},a.NAME="generic",a.OPTIONS={},a);function a(t){void 0===t&&(t={}),this.adaptor=null,this.mmlFactory=null;var e=this.constructor;this.options=n.userOptions(n.defaultOptions({},e.OPTIONS),t),this.preFilters=new i.FunctionList,this.postFilters=new i.FunctionList}e.AbstractInputJax=o},function(t,e,r){"use strict";var a=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},n=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var c=r(4),l=(Object.defineProperty(u.prototype,"nodes",{get:function(){return this._nodes},enumerable:!0,configurable:!0}),u.prototype.Push=function(){for(var t,e=[],r=0;rt.configuration.options.maxMacros)throw new d.default("MaxMacroSub2","MathJax maximum substitution count exceeded; is there a recursive latex environment?");t.parse("environment",[t,r])},i.Array=function(t,e,r,n,i,o,a,s,c){var l=("c"+(i=i||t.GetArgument("\\begin{"+e.getName()+"}"))).replace(/[^clr|:]/g,"").replace(/[^|:]([|:])+/g,"$1");i=(i=i.replace(/[^clr]/g,"").split("").join(" ")).replace(/l/g,"left").replace(/r/g,"right").replace(/c/g,"center");var u=t.itemFactory.create("array");return u.arraydef={columnalign:i,columnspacing:o||"1em",rowspacing:a||"4pt"},l.match(/[|:]/)&&(l.charAt(0).match(/[|:]/)&&(u.frame.push("left"),u.dashed=":"===l.charAt(0)),l.charAt(l.length-1).match(/[|:]/)&&u.frame.push("right"),l=l.substr(1,l.length-2),u.arraydef.columnlines=l.split("").join(" ").replace(/[^|: ]/g,"none").replace(/\|/g,"solid").replace(/:/g,"dashed")),r&&u.setProperty("open",t.convertDelimiter(r)),n&&u.setProperty("close",t.convertDelimiter(n)),"D"===s?u.arraydef.displaystyle=!0:s&&(u.arraydef.displaystyle=!1),"S"===s&&(u.arraydef.scriptlevel=1),c&&(u.arraydef.useHeight=!1),t.Push(e),u},i.AlignedArray=function(t,e){var r=t.GetBrackets("\\begin{"+e.getName()+"}"),n=i.Array(t,e);return y.default.setArrayAlign(n,r)},i.Equation=function(t,e,r){return t.Push(e),y.default.checkEqnEnv(t),t.itemFactory.create("equation",r).setProperty("name",e.getName())},i.EqnArray=function(t,e,r,n,i,o){t.Push(e),n&&y.default.checkEqnEnv(t),i=(i=i.replace(/[^clr]/g,"").split("").join(" ")).replace(/l/g,"left").replace(/r/g,"right").replace(/c/g,"center");var a=t.itemFactory.create("eqnarray",e.getName(),r,n,t.stack.global);return a.arraydef={displaystyle:!0,columnalign:i,columnspacing:o||"1em",rowspacing:"3pt",side:t.options.tagSide,minlabelspacing:t.options.tagIndent},a},i.HandleNoTag=function(t,e){t.tags.notag()},i.HandleLabel=function(t,e){t.stack.global;var r=t.GetArgument(e);if(""!==r&&!t.tags.refUpdate){if(t.tags.label)throw new d.default("MultipleCommand","Multiple %1",t.currentCS);if(t.tags.label=r,(t.tags.allLabels[r]||t.tags.labels[r])&&!t.options.ignoreDuplicateLabels)throw new d.default("MultipleLabel","Label '%1' multiply defined",r);t.tags.labels[r]=new s.Label}},i.HandleRef=function(t,e,r){var n=t.GetArgument(e),i=t.tags.allLabels[n]||t.tags.labels[n];i||(t.tags.refUpdate||(t.tags.redo=!0),i=new s.Label);var o=i.tag;r&&(o=t.tags.formatTag(o));var a=t.create("node","mrow",y.default.internalMath(t,o),{href:t.tags.formatUrl(i.id,t.options.baseURL),class:"MathJax_ref"});t.Push(a)},i.Macro=function(t,e,r,n,i){if(n){var o=[];if(null!=i){var a=t.GetBrackets(e);o.push(null==a?i:a)}for(var s=o.length;st.configuration.options.maxMacros)throw new d.default("MaxMacroSub1","MathJax maximum macro substitution count exceeded; is there a recursive macro call?")},i.MathChoice=function(t,e){var r=t.ParseArg(e),n=t.ParseArg(e),i=t.ParseArg(e),o=t.ParseArg(e);t.Push(t.create("node","mathchoice",[r,n,i,o]))},e.default=i},function(t,p,e){"use strict";var d=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0Math.PI/2-r?t.thickness*h*Math.sin(u+r-Math.PI/2):0);return[f,p,f,p]},remove:e[3]}]}},p.CommonArrow=function(f){return function(t){var e=d(p.arrowDef[t],4),l=e[0],u=e[1],h=e[2],r=e[3];return[t+"arrow",{renderer:function(t,e){var r=t.getBBox(),n=r.w,i=r.h,o=r.d,a=d(h?[i+o,n]:[n,i+o],2),s=a[0],c=(a[1],t.arrow(s,l,u));f(t,c)},bbox:p.arrowBBox[t],remove:r}]}}},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),h=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0 *":{display:"block"}},g.useIC=!1,g);function g(){return null!==v&&v.apply(this,arguments)||this}e.CHTMLmsubsup=b},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),f=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},p=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=e&&a.item.renderDoc(t))return}}catch(t){r={error:t}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}},v.prototype.renderMath=function(t,e,r){var n,i;void 0===r&&(r=m.STATE.UNPROCESSED);try{for(var o=h(this.items),a=o.next();!a.done;a=o.next()){var s=a.value;if(s.priority>=r&&s.item.renderMath(t,e))return}}catch(t){n={error:t}}finally{try{a&&!a.done&&(i=o.return)&&i.call(o)}finally{if(n)throw n.error}}},v.prototype.renderConvert=function(t,e,r){var n,i;void 0===r&&(r=m.STATE.LAST);try{for(var o=h(this.items),a=o.next();!a.done;a=o.next()){var s=a.value;if(s.priority>=r)return;if(s.item.convert&&s.item.renderMath(t,e))return}}catch(t){n={error:t}}finally{try{a&&!a.done&&(i=o.return)&&i.call(o)}finally{if(n)throw n.error}}},v.prototype.findID=function(t){var e,r;try{for(var n=h(this.items),i=n.next();!i.done;i=n.next()){var o=i.value;if(o.item.id===t)return o.item}}catch(t){e={error:t}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(e)throw e.error}}return null},v);function v(){return null!==o&&o.apply(this,arguments)||this}e.RenderList=y;var b,g=(b=a.AbstractInputJax,i(M,b),M.prototype.compile=function(t){return null},M);function M(){return null!==b&&b.apply(this,arguments)||this}var O,x=(O=s.AbstractOutputJax,i(S,O),S.prototype.typeset=function(t,e){return void 0===e&&(e=null),null},S.prototype.escaped=function(t,e){return null},S);function S(){return null!==O&&O.apply(this,arguments)||this}var E,C=(E=c.AbstractMathList,i(_,E),_);function _(){return null!==E&&E.apply(this,arguments)||this}var T,w=(T=m.AbstractMathItem,i(A,T),A);function A(){return null!==T&&T.apply(this,arguments)||this}var k=(Object.defineProperty(I.prototype,"kind",{get:function(){return this.constructor.KIND},enumerable:!0,configurable:!0}),I.prototype.addRenderAction=function(t){for(var e=[],r=1;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var o=(Object.defineProperty(a.prototype,"factory",{get:function(){return this._factory},enumerable:!0,configurable:!0}),Object.defineProperty(a.prototype,"kind",{get:function(){return"unknown"},enumerable:!0,configurable:!0}),a.prototype.setProperty=function(t,e){this.properties[t]=e},a.prototype.getProperty=function(t){return this.properties[t]},a.prototype.getPropertyNames=function(){return Object.keys(this.properties)},a.prototype.getAllProperties=function(){return this.properties},a.prototype.removeProperty=function(){for(var e,t,r=[],n=0;n=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var a,u=r(0),s=(a=u.AbstractMmlNode,i(c,a),Object.defineProperty(c.prototype,"kind",{get:function(){return"mrow"},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"isSpacelike",{get:function(){var e,t;try{for(var r=l(this.childNodes),n=r.next();!n.done;n=r.next())if(!n.value.isSpacelike)return!1}catch(t){e={error:t}}finally{try{n&&!n.done&&(t=r.return)&&t.call(r)}finally{if(e)throw e.error}}return!0},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"isEmbellished",{get:function(){var e,t,r=!1,n=0;try{for(var i=l(this.childNodes),o=i.next();!o.done;o=i.next()){var a=o.value;if(a)if(a.isEmbellished){if(r)return!1;r=!0,this._core=n}else if(!a.isSpacelike)return!1;n++}}catch(t){e={error:t}}finally{try{o&&!o.done&&(t=i.return)&&t.call(i)}finally{if(e)throw e.error}}return r},enumerable:!0,configurable:!0}),c.prototype.core=function(){return this.isEmbellished&&null!=this._core?this.childNodes[this._core]:this},c.prototype.coreMO=function(){return this.isEmbellished&&null!=this._core?this.childNodes[this._core].coreMO():this},c.prototype.nonSpaceLength=function(){var e,t,r=0;try{for(var n=l(this.childNodes),i=n.next();!i.done;i=n.next()){var o=i.value;o&&!o.isSpacelike&&r++}}catch(t){e={error:t}}finally{try{i&&!i.done&&(t=n.return)&&t.call(n)}finally{if(e)throw e.error}}return r},c.prototype.firstNonSpace=function(){var e,t;try{for(var r=l(this.childNodes),n=r.next();!n.done;n=r.next()){var i=n.value;if(i&&!i.isSpacelike)return i}}catch(t){e={error:t}}finally{try{n&&!n.done&&(t=r.return)&&t.call(r)}finally{if(e)throw e.error}}return null},c.prototype.lastNonSpace=function(){for(var t=this.childNodes.length;0<=--t;){var e=this.childNodes[t];if(e&&!e.isSpacelike)return e}return null},c.prototype.setTeXclass=function(t){var e,r,n,i;if(null==this.getProperty("open")&&null==this.getProperty("close")||t&&null==t.getProperty("fnOp")){try{for(var o=l(this.childNodes),a=o.next();!a.done;a=o.next())t=a.value.setTeXclass(t)}catch(t){n={error:t}}finally{try{a&&!a.done&&(i=o.return)&&i.call(o)}finally{if(n)throw n.error}}this.childNodes[0]&&this.updateTeXclass(this.childNodes[0])}else{this.getPrevClass(t),t=null;try{for(var s=l(this.childNodes),c=s.next();!c.done;c=s.next())t=c.value.setTeXclass(t)}catch(t){e={error:t}}finally{try{c&&!c.done&&(r=s.return)&&r.call(s)}finally{if(e)throw e.error}}null==this.texClass&&(this.texClass=u.TEXCLASS.INNER)}return t},c.defaults=o({},u.AbstractMmlNode.defaults),c);function c(){var t=null!==a&&a.apply(this,arguments)||this;return t._core=null,t}e.MmlMrow=s;var h,f=(i(p,h=s),Object.defineProperty(p.prototype,"kind",{get:function(){return"inferredMrow"},enumerable:!0,configurable:!0}),Object.defineProperty(p.prototype,"isInferred",{get:function(){return!0},enumerable:!0,configurable:!0}),Object.defineProperty(p.prototype,"notParent",{get:function(){return!0},enumerable:!0,configurable:!0}),p.prototype.toString=function(){return"["+this.childNodes.join(",")+"]"},p.defaults=s.defaults,p);function p(){return null!==h&&h.apply(this,arguments)||this}e.MmlInferredMrow=f},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),o=this&&this.__assign||function(){return(o=Object.assign||function(t){for(var e,r=1,n=arguments.length;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var s,c=r(0),l=(s=c.AbstractMmlBaseNode,i(u,s),Object.defineProperty(u.prototype,"kind",{get:function(){return"mfrac"},enumerable:!0,configurable:!0}),Object.defineProperty(u.prototype,"arity",{get:function(){return 2},enumerable:!0,configurable:!0}),Object.defineProperty(u.prototype,"linebreakContainer",{get:function(){return!0},enumerable:!0,configurable:!0}),u.prototype.setTeXclass=function(t){var e,r;this.getPrevClass(t);try{for(var n=a(this.childNodes),i=n.next();!i.done;i=n.next())i.value.setTeXclass(null)}catch(t){e={error:t}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(e)throw e.error}}return this.isEmbellished&&this.updateTeXclass(this.core()),this},u.prototype.setChildInheritedAttributes=function(t,e,r,n){(!e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var u,h=r(0),a=(u=h.AbstractMmlNode,i(s,u),Object.defineProperty(s.prototype,"kind",{get:function(){return"mfenced"},enumerable:!0,configurable:!0}),s.prototype.setTeXclass=function(t){this.getPrevClass(t),this.open&&(t=this.open.setTeXclass(t)),this.childNodes[0]&&(t=this.childNodes[0].setTeXclass(t));for(var e=1,r=this.childNodes.length;ethis.childNodes.length&&(t=1),this.attributes.set("selection",t)},l.defaults=o(o({},s.AbstractMmlNode.defaults),{actiontype:"toggle",selection:1}),l);function l(){return null!==a&&a.apply(this,arguments)||this}e.MmlMaction=c},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),o=this&&this.__assign||function(){return(o=Object.assign||function(t){for(var e,r=1,n=arguments.length;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var l,u=r(0),m=r(10),a=(l=u.AbstractMmlNode,i(s,l),Object.defineProperty(s.prototype,"kind",{get:function(){return"mtable"},enumerable:!0,configurable:!0}),Object.defineProperty(s.prototype,"linebreakContainer",{get:function(){return!0},enumerable:!0,configurable:!0}),s.prototype.setInheritedAttributes=function(t,e,r,n){var i,o;try{for(var a=d(u.indentAttributes),s=a.next();!s.done;s=a.next()){var c=s.value;t[c]&&this.attributes.setInherited(c,t[c][1]),void 0!==this.attributes.getExplicit(c)&&delete this.attributes.getAllAttributes()[c]}}catch(t){i={error:t}}finally{try{s&&!s.done&&(o=a.return)&&o.call(a)}finally{if(i)throw i.error}}l.prototype.setInheritedAttributes.call(this,t,e,r,n)},s.prototype.setChildInheritedAttributes=function(t,e,r,n){var i,o,a,s;try{for(var c=d(this.childNodes),l=c.next();!l.done;l=c.next())(p=l.value).isKind("mtr")||this.replaceChild(this.factory.create("mtr"),p).appendChild(p)}catch(t){i={error:t}}finally{try{l&&!l.done&&(o=c.return)&&o.call(c)}finally{if(i)throw i.error}}e=!(!this.attributes.getExplicit("displaystyle")&&!this.attributes.getDefault("displaystyle")),t=this.addInheritedAttributes(t,{columnalign:this.attributes.get("columnalign"),rowalign:"center"});var u=m.split(this.attributes.get("rowalign"));try{for(var h=d(this.childNodes),f=h.next();!f.done;f=h.next()){var p=f.value;t.rowalign[1]=u.shift()||t.rowalign[1],p.setInheritedAttributes(t,e,r,n)}}catch(t){a={error:t}}finally{try{f&&!f.done&&(s=h.return)&&s.call(h)}finally{if(a)throw a.error}}},s.prototype.verifyChildren=function(t){var e,r;if(!t.fixMtables)try{for(var n=d(this.childNodes),i=n.next();!i.done;i=n.next())i.value.isKind("mtr")||this.mError("Children of "+this.kind+" must be mtr or mlabeledtr",t)}catch(t){e={error:t}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(e)throw e.error}}l.prototype.verifyChildren.call(this,t)},s.prototype.setTeXclass=function(t){var e,r;this.getPrevClass(t);try{for(var n=d(this.childNodes),i=n.next();!i.done;i=n.next())i.value.setTeXclass(null)}catch(t){e={error:t}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(e)throw e.error}}return this},s.defaults=o(o({},u.AbstractMmlNode.defaults),{align:"axis",rowalign:"baseline",columnalign:"center",groupalign:"{left}",alignmentscope:!0,columnwidth:"auto",width:"auto",rowspacing:"1ex",columnspacing:".8em",rowlines:"none",columnlines:"none",frame:"none",framespacing:"0.4em 0.5ex",equalrows:!1,equalcolumns:!1,displaystyle:!1,side:"right",minlabelspacing:"0.8em"}),s);function s(){var t=null!==l&&l.apply(this,arguments)||this;return t.properties={useHeight:1},t.texClass=u.TEXCLASS.ORD,t}e.MmlMtable=a},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),o=this&&this.__assign||function(){return(o=Object.assign||function(t){for(var e,r=1,n=arguments.length;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var a,s=r(0),c=r(19),m=r(10),l=(a=s.AbstractMmlNode,i(u,a),Object.defineProperty(u.prototype,"kind",{get:function(){return"mtr"},enumerable:!0,configurable:!0}),Object.defineProperty(u.prototype,"linebreakContainer",{get:function(){return!0},enumerable:!0,configurable:!0}),u.prototype.setChildInheritedAttributes=function(t,e,r,n){var i,o,a,s;try{for(var c=d(this.childNodes),l=c.next();!l.done;l=c.next())(p=l.value).isKind("mtd")||this.replaceChild(this.factory.create("mtd"),p).appendChild(p)}catch(t){i={error:t}}finally{try{l&&!l.done&&(o=c.return)&&o.call(c)}finally{if(i)throw i.error}}var u=m.split(this.attributes.get("columnalign"));1===this.arity&&u.unshift(this.parent.attributes.get("side")),t=this.addInheritedAttributes(t,{rowalign:this.attributes.get("rowalign"),columnalign:"center"});try{for(var h=d(this.childNodes),f=h.next();!f.done;f=h.next()){var p=f.value;t.columnalign[1]=u.shift()||t.columnalign[1],p.setInheritedAttributes(t,e,r,n)}}catch(t){a={error:t}}finally{try{f&&!f.done&&(s=h.return)&&s.call(h)}finally{if(a)throw a.error}}},u.prototype.verifyChildren=function(t){var e,r;if(!this.parent||this.parent.isKind("mtable")){if(!t.fixMtables)try{for(var n=d(this.childNodes),i=n.next();!i.done;i=n.next()){var o=i.value;o.isKind("mtd")||this.replaceChild(this.factory.create("mtr"),o).mError("Children of "+this.kind+" must be mtd",t,!0)}}catch(t){e={error:t}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(e)throw e.error}}a.prototype.verifyChildren.call(this,t)}else this.mError(this.kind+" can only be a child of an mtable",t,!0)},u.prototype.setTeXclass=function(t){var e,r;this.getPrevClass(t);try{for(var n=d(this.childNodes),i=n.next();!i.done;i=n.next())i.value.setTeXclass(null)}catch(t){e={error:t}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(e)throw e.error}}return this},u.defaults=o(o({},s.AbstractMmlNode.defaults),{rowalign:c.INHERIT,columnalign:c.INHERIT,groupalign:c.INHERIT}),u);function u(){return null!==a&&a.apply(this,arguments)||this}e.MmlMtr=l;var h,f=(i(p,h=l),Object.defineProperty(p.prototype,"kind",{get:function(){return"mlabeledtr"},enumerable:!0,configurable:!0}),Object.defineProperty(p.prototype,"arity",{get:function(){return 1},enumerable:!0,configurable:!0}),p);function p(){return null!==h&&h.apply(this,arguments)||this}e.MmlMlabeledtr=f},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),o=this&&this.__assign||function(){return(o=Object.assign||function(t){for(var e,r=1,n=arguments.length;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},v=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0t.configuration.options.maxMacros)throw new l.default("MaxMacroSub1","MathJax maximum macro substitution count exceeded; is here a recursive macro call?")},BeginEnv:function(t,e,r,n,i,o){if(e.getProperty("end")&&t.stack.env.closing===e.getName()){delete t.stack.env.closing;var a=t.string.slice(t.i);return t.string=n,t.i=0,t.Parse(),t.string=a,t.i=0,t.itemFactory.create("end").setProperty("name",e.getName())}if(i){var s=[];if(null!=o){var c=t.GetBrackets("\\begin{"+e.getName()+"}");s.push(null==c?o:c)}for(var l=s.length;l=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var l=r(16);e.CommonMrowMixin=function(t){return i(e,s=t),Object.defineProperty(e.prototype,"fixesPWidth",{get:function(){return!1},enumerable:!0,configurable:!0}),e.prototype.stretchChildren=function(){var e,t,r,n,i,o,a=[];try{for(var s=S(this.childNodes),c=s.next();!c.done;c=s.next())(x=c.value).canStretch(1)&&a.push(x)}catch(t){e={error:t}}finally{try{c&&!c.done&&(t=s.return)&&t.call(s)}finally{if(e)throw e.error}}var l=a.length,u=this.childNodes.length;if(l&&1 mjx-box":{"border-top":".07em solid"},"mjx-sqrt.mjx-tall > mjx-box":{"padding-left":".3em","margin-left":"-.3em"}},u);function u(){return null!==o&&o.apply(this,arguments)||this}e.CHTMLmsqrt=l},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),C=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0}),e.CommonMtrMixin=function(t){return i(e,r=t),Object.defineProperty(e.prototype,"fixesPWidth",{get:function(){return!1},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"numCells",{get:function(){return this.childNodes.length},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"labeled",{get:function(){return!1},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"tableCells",{get:function(){return this.childNodes},enumerable:!0,configurable:!0}),e.prototype.getChild=function(t){return this.childNodes[t]},e.prototype.getChildBBoxes=function(){return this.childNodes.map(function(t){return t.getBBox()})},e.prototype.stretchChildren=function(t){var e,r,n,i,o,a;void 0===t&&(t=null);var s=[],c=this.labeled?this.childNodes.slice(1):this.childNodes;try{for(var l=C(c),u=l.next();!u.done;u=l.next())(E=u.value.childNodes[0]).canStretch(1)&&s.push(E)}catch(t){e={error:t}}finally{try{u&&!u.done&&(r=l.return)&&r.call(l)}finally{if(e)throw e.error}}var h=s.length,f=this.childNodes.length;if(h&&1=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(80),s=(o=a.AbstractDOMAdaptor,i(l,o),l.prototype.parse=function(t,e){return void 0===e&&(e="text/html"),this.parser.parseFromString(t,e)},l.prototype.create=function(t,e){return e?this.document.createElementNS(e,t):this.document.createElement(t)},l.prototype.text=function(t){return this.document.createTextNode(t)},l.prototype.head=function(t){return t.head},l.prototype.body=function(t){return t.body},l.prototype.root=function(t){return t.documentElement},l.prototype.tags=function(t,e,r){void 0===r&&(r=null);var n=r?t.getElementsByTagNameNS(r,e):t.getElementsByTagName(e);return Array.from(n)},l.prototype.getElements=function(t,e){var r,n,i=[];try{for(var o=c(t),a=o.next();!a.done;a=o.next()){var s=a.value;"string"==typeof s?i=i.concat(Array.from(this.document.querySelectorAll(s))):Array.isArray(s)?i=i.concat(Array.from(s)):s instanceof this.window.NodeList||s instanceof this.window.HTMLCollection?i=i.concat(Array.from(s)):i.push(s)}}catch(t){r={error:t}}finally{try{a&&!a.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}return i},l.prototype.parent=function(t){return t.parentNode},l.prototype.append=function(t,e){return t.appendChild(e)},l.prototype.insert=function(t,e){return this.parent(e).insertBefore(t,e)},l.prototype.remove=function(t){return this.parent(t).removeChild(t)},l.prototype.replace=function(t,e){return this.parent(e).replaceChild(t,e)},l.prototype.clone=function(t){return t.cloneNode(!0)},l.prototype.split=function(t,e){return t.splitText(e)},l.prototype.next=function(t){return t.nextSibling},l.prototype.previous=function(t){return t.previousSibling},l.prototype.firstChild=function(t){return t.firstChild},l.prototype.lastChild=function(t){return t.lastChild},l.prototype.childNodes=function(t){return Array.from(t.childNodes)},l.prototype.childNode=function(t,e){return t.childNodes[e]},l.prototype.kind=function(t){return t.nodeName.toLowerCase()},l.prototype.value=function(t){return t.nodeValue||""},l.prototype.textContent=function(t){return t.textContent},l.prototype.innerHTML=function(t){return t.innerHTML},l.prototype.outerHTML=function(t){return t.outerHTML},l.prototype.setAttribute=function(t,e,r,n){return void 0===n&&(n=null),n?t.setAttributeNS(n,e,r):t.setAttribute(e,r)},l.prototype.getAttribute=function(t,e){return t.getAttribute(e)},l.prototype.removeAttribute=function(t,e){return t.removeAttribute(e)},l.prototype.hasAttribute=function(t,e){return t.hasAttribute(e)},l.prototype.allAttributes=function(t){return Array.from(t.attributes).map(function(t){return{name:t.name,value:t.value}})},l.prototype.addClass=function(t,e){t.classList.add(e)},l.prototype.removeClass=function(t,e){return t.classList.remove(e)},l.prototype.hasClass=function(t,e){return t.classList.contains(e)},l.prototype.setStyle=function(t,e,r){t.style[e]=r},l.prototype.getStyle=function(t,e){return t.style[e]},l.prototype.allStyles=function(t){return t.style.cssText},l.prototype.fontSize=function(t){var e=this.window.getComputedStyle(t);return parseFloat(e.fontSize)},l.prototype.nodeSize=function(t,e,r){if(void 0===e&&(e=1),void 0===r&&(r=!1),r&&t.getBBox){var n=t.getBBox();return[n.width/e,n.height/e]}return[t.offsetWidth/e,t.offsetHeight/e]},l.prototype.nodeBBox=function(t){var e=t.getBoundingClientRect();return{left:e.left,right:e.right,top:e.top,bottom:e.bottom}},l);function l(t){var e=o.call(this,t.document)||this;return e.window=t,e.parser=new t.DOMParser,e}e.HTMLAdaptor=s},function(t,e,r){"use strict";var m=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var n=(i.prototype.node=function(t,e,r,n){var i,o;void 0===e&&(e={}),void 0===r&&(r=[]);var a=this.create(t,n);this.setAttributes(a,e);try{for(var s=m(r),c=s.next();!c.done;c=s.next()){var l=c.value;this.append(a,l)}}catch(t){i={error:t}}finally{try{c&&!c.done&&(o=s.return)&&o.call(s)}finally{if(i)throw i.error}}return a},i.prototype.setAttributes=function(t,e){var r,n,i,o,a,s;if(e.style&&"string"!=typeof e.style)try{for(var c=m(Object.keys(e.style)),l=c.next();!l.done;l=c.next()){var u=l.value;this.setStyle(t,u.replace(/-([a-z])/g,function(t,e){return e.toUpperCase()}),e.style[u])}}catch(t){r={error:t}}finally{try{l&&!l.done&&(n=c.return)&&n.call(c)}finally{if(r)throw r.error}}if(e.properties)try{for(var h=m(Object.keys(e.properties)),f=h.next();!f.done;f=h.next())t[u=f.value]=e.properties[u]}catch(t){i={error:t}}finally{try{f&&!f.done&&(o=h.return)&&o.call(h)}finally{if(i)throw i.error}}try{for(var p=m(Object.keys(e)),d=p.next();!d.done;d=p.next())"style"===(u=d.value)&&"string"!=typeof e.style||"properties"===u||this.setAttribute(t,u,e[u])}catch(t){a={error:t}}finally{try{d&&!d.done&&(s=p.return)&&s.call(p)}finally{if(a)throw a.error}}},i.prototype.replace=function(t,e){return this.insert(t,e),this.remove(e),e},i.prototype.childNode=function(t,e){return this.childNodes(t)[e]},i.prototype.allClasses=function(t){var e=this.getAttribute(t,"class");return e?e.replace(/ +/g," ").replace(/^ /,"").replace(/ $/,"").split(/ /):[]},i);function i(t){void 0===t&&(t=null),this.document=t}e.AbstractDOMAdaptor=n},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(79);e.browserAdaptor=function(){return new n.HTMLAdaptor(window)}},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(41),s=(o=a.AbstractMathDocument,i(c,o),c);function c(){return null!==o&&o.apply(this,arguments)||this}var l=(Object.defineProperty(u.prototype,"name",{get:function(){return this.constructor.NAME},enumerable:!0,configurable:!0}),u.prototype.handlesDocument=function(t){return!1},u.prototype.create=function(t,e){return new this.documentClass(t,this.adaptor,e)},u.NAME="generic",u);function u(t,e){void 0===e&&(e=5),this.documentClass=s,this.adaptor=t,this.priority=e}e.AbstractHandler=l},function(t,e,r){"use strict";var l=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var h=Symbol(),c=function(t){void 0===t&&(t=null),this.next=null,this.prev=null,this.data=t};e.ListItem=c;var i=(f.prototype.toArray=function(){return Array.from(this)},f.prototype.isBefore=function(t,e){return t":e.MO.BIN5,".":[0,3,i.TEXCLASS.PUNCT,{separator:!0}],"/":e.MO.ORD11,"//":n(1,1),"/=":e.MO.BIN4,":":[1,2,i.TEXCLASS.REL,null],":=":e.MO.BIN4,";":[0,3,i.TEXCLASS.PUNCT,{linebreakstyle:"after",separator:!0}],"<":e.MO.REL,"<=":e.MO.BIN5,"<>":n(1,1),"=":e.MO.REL,"==":e.MO.BIN4,">":e.MO.REL,">=":e.MO.BIN5,"?":[1,1,i.TEXCLASS.CLOSE,null],"@":e.MO.ORD11,"\\":e.MO.ORD,"^":e.MO.ORD11,_:e.MO.ORD11,"|":[2,2,i.TEXCLASS.ORD,{fence:!0,stretchy:!0,symmetric:!0}],"||":[2,2,i.TEXCLASS.BIN,{fence:!0,stretchy:!0,symmetric:!0}],"|||":[2,2,i.TEXCLASS.ORD,{fence:!0,stretchy:!0,symmetric:!0}],"\xb1":e.MO.BIN4,"\xb7":e.MO.BIN4,"\xd7":e.MO.BIN4,"\xf7":e.MO.BIN4,"\u02b9":e.MO.ORD,"\u0300":e.MO.ACCENT,"\u0301":e.MO.ACCENT,"\u0303":e.MO.WIDEACCENT,"\u0304":e.MO.ACCENT,"\u0306":e.MO.ACCENT,"\u0307":e.MO.ACCENT,"\u0308":e.MO.ACCENT,"\u030c":e.MO.ACCENT,"\u0332":e.MO.WIDEACCENT,"\u0338":e.MO.REL4,"\u2015":[0,0,i.TEXCLASS.ORD,{stretchy:!0}],"\u2017":[0,0,i.TEXCLASS.ORD,{stretchy:!0}],"\u2020":e.MO.BIN3,"\u2021":e.MO.BIN3,"\u2022":e.MO.BIN4,"\u2026":e.MO.INNER,"\u2044":e.MO.TALLBIN,"\u2061":e.MO.ORD,"\u2062":e.MO.ORD,"\u2063":[0,0,i.TEXCLASS.ORD,{linebreakstyle:"after",separator:!0}],"\u2064":e.MO.ORD,"\u20d7":e.MO.ACCENT,"\u2111":e.MO.ORD,"\u2113":e.MO.ORD,"\u2118":e.MO.ORD,"\u211c":e.MO.ORD,"\u2190":e.MO.WIDEREL,"\u2191":e.MO.RELSTRETCH,"\u2192":e.MO.WIDEREL,"\u2193":e.MO.RELSTRETCH,"\u2194":e.MO.WIDEREL,"\u2195":e.MO.RELSTRETCH,"\u2196":e.MO.RELSTRETCH,"\u2197":e.MO.RELSTRETCH,"\u2198":e.MO.RELSTRETCH,"\u2199":e.MO.RELSTRETCH,"\u219a":e.MO.RELACCENT,"\u219b":e.MO.RELACCENT,"\u219c":e.MO.WIDEREL,"\u219d":e.MO.WIDEREL,"\u219e":e.MO.WIDEREL,"\u219f":e.MO.WIDEREL,"\u21a0":e.MO.WIDEREL,"\u21a1":e.MO.RELSTRETCH,"\u21a2":e.MO.WIDEREL,"\u21a3":e.MO.WIDEREL,"\u21a4":e.MO.WIDEREL,"\u21a5":e.MO.RELSTRETCH,"\u21a6":e.MO.WIDEREL,"\u21a7":e.MO.RELSTRETCH,"\u21a8":e.MO.RELSTRETCH,"\u21a9":e.MO.WIDEREL,"\u21aa":e.MO.WIDEREL,"\u21ab":e.MO.WIDEREL,"\u21ac":e.MO.WIDEREL,"\u21ad":e.MO.WIDEREL,"\u21ae":e.MO.RELACCENT,"\u21af":e.MO.RELSTRETCH,"\u21b0":e.MO.RELSTRETCH,"\u21b1":e.MO.RELSTRETCH,"\u21b2":e.MO.RELSTRETCH,"\u21b3":e.MO.RELSTRETCH,"\u21b4":e.MO.RELSTRETCH,"\u21b5":e.MO.RELSTRETCH,"\u21b6":e.MO.RELACCENT,"\u21b7":e.MO.RELACCENT,"\u21b8":e.MO.REL,"\u21b9":e.MO.WIDEREL,"\u21ba":e.MO.REL,"\u21bb":e.MO.REL,"\u21bc":e.MO.WIDEREL,"\u21bd":e.MO.WIDEREL,"\u21be":e.MO.RELSTRETCH,"\u21bf":e.MO.RELSTRETCH,"\u21c0":e.MO.WIDEREL,"\u21c1":e.MO.WIDEREL,"\u21c2":e.MO.RELSTRETCH,"\u21c3":e.MO.RELSTRETCH,"\u21c4":e.MO.WIDEREL,"\u21c5":e.MO.RELSTRETCH,"\u21c6":e.MO.WIDEREL,"\u21c7":e.MO.WIDEREL,"\u21c8":e.MO.RELSTRETCH,"\u21c9":e.MO.WIDEREL,"\u21ca":e.MO.RELSTRETCH,"\u21cb":e.MO.WIDEREL,"\u21cc":e.MO.WIDEREL,"\u21cd":e.MO.RELACCENT,"\u21ce":e.MO.RELACCENT,"\u21cf":e.MO.RELACCENT,"\u21d0":e.MO.WIDEREL,"\u21d1":e.MO.RELSTRETCH,"\u21d2":e.MO.WIDEREL,"\u21d3":e.MO.RELSTRETCH,"\u21d4":e.MO.WIDEREL,"\u21d5":e.MO.RELSTRETCH,"\u21d6":e.MO.RELSTRETCH,"\u21d7":e.MO.RELSTRETCH,"\u21d8":e.MO.RELSTRETCH,"\u21d9":e.MO.RELSTRETCH,"\u21da":e.MO.WIDEREL,"\u21db":e.MO.WIDEREL,"\u21dc":e.MO.WIDEREL,"\u21dd":e.MO.WIDEREL,"\u21de":e.MO.REL,"\u21df":e.MO.REL,"\u21e0":e.MO.WIDEREL,"\u21e1":e.MO.RELSTRETCH,"\u21e2":e.MO.WIDEREL,"\u21e3":e.MO.RELSTRETCH,"\u21e4":e.MO.WIDEREL,"\u21e5":e.MO.WIDEREL,"\u21e6":e.MO.WIDEREL,"\u21e7":e.MO.RELSTRETCH,"\u21e8":e.MO.WIDEREL,"\u21e9":e.MO.RELSTRETCH,"\u21ea":e.MO.RELSTRETCH,"\u21eb":e.MO.RELSTRETCH,"\u21ec":e.MO.RELSTRETCH,"\u21ed":e.MO.RELSTRETCH,"\u21ee":e.MO.RELSTRETCH,"\u21ef":e.MO.RELSTRETCH,"\u21f0":e.MO.WIDEREL,"\u21f1":e.MO.REL,"\u21f2":e.MO.REL,"\u21f3":e.MO.RELSTRETCH,"\u21f4":e.MO.RELACCENT,"\u21f5":e.MO.RELSTRETCH,"\u21f6":e.MO.WIDEREL,"\u21f7":e.MO.RELACCENT,"\u21f8":e.MO.RELACCENT,"\u21f9":e.MO.RELACCENT,"\u21fa":e.MO.RELACCENT,"\u21fb":e.MO.RELACCENT,"\u21fc":e.MO.RELACCENT,"\u21fd":e.MO.WIDEREL,"\u21fe":e.MO.WIDEREL,"\u21ff":e.MO.WIDEREL,"\u2201":n(1,2,i.TEXCLASS.ORD),"\u2205":e.MO.ORD,"\u2206":e.MO.BIN3,"\u2208":e.MO.REL,"\u2209":e.MO.REL,"\u220a":e.MO.REL,"\u220b":e.MO.REL,"\u220c":e.MO.REL,"\u220d":e.MO.REL,"\u220e":e.MO.BIN3,"\u2212":e.MO.BIN4,"\u2213":e.MO.BIN4,"\u2214":e.MO.BIN4,"\u2215":e.MO.TALLBIN,"\u2216":e.MO.BIN4,"\u2217":e.MO.BIN4,"\u2218":e.MO.BIN4,"\u2219":e.MO.BIN4,"\u221d":e.MO.REL,"\u221e":e.MO.ORD,"\u221f":e.MO.REL,"\u2223":e.MO.REL,"\u2224":e.MO.REL,"\u2225":e.MO.REL,"\u2226":e.MO.REL,"\u2227":e.MO.BIN4,"\u2228":e.MO.BIN4,"\u2229":e.MO.BIN4,"\u222a":e.MO.BIN4,"\u2234":e.MO.REL,"\u2235":e.MO.REL,"\u2236":e.MO.REL,"\u2237":e.MO.REL,"\u2238":e.MO.BIN4,"\u2239":e.MO.REL,"\u223a":e.MO.BIN4,"\u223b":e.MO.REL,"\u223c":e.MO.REL,"\u223d":e.MO.REL,"\u223d\u0331":e.MO.BIN3,"\u223e":e.MO.REL,"\u223f":e.MO.BIN3,"\u2240":e.MO.BIN4,"\u2241":e.MO.REL,"\u2242":e.MO.REL,"\u2242\u0338":e.MO.REL,"\u2243":e.MO.REL,"\u2244":e.MO.REL,"\u2245":e.MO.REL,"\u2246":e.MO.REL,"\u2247":e.MO.REL,"\u2248":e.MO.REL,"\u2249":e.MO.REL,"\u224a":e.MO.REL,"\u224b":e.MO.REL,"\u224c":e.MO.REL,"\u224d":e.MO.REL,"\u224e":e.MO.REL,"\u224e\u0338":e.MO.REL,"\u224f":e.MO.REL,"\u224f\u0338":e.MO.REL,"\u2250":e.MO.REL,"\u2251":e.MO.REL,"\u2252":e.MO.REL,"\u2253":e.MO.REL,"\u2254":e.MO.REL,"\u2255":e.MO.REL,"\u2256":e.MO.REL,"\u2257":e.MO.REL,"\u2258":e.MO.REL,"\u2259":e.MO.REL,"\u225a":e.MO.REL,"\u225c":e.MO.REL,"\u225d":e.MO.REL,"\u225e":e.MO.REL,"\u225f":e.MO.REL,"\u2260":e.MO.REL,"\u2261":e.MO.REL,"\u2262":e.MO.REL,"\u2263":e.MO.REL,"\u2264":e.MO.REL,"\u2265":e.MO.REL,"\u2266":e.MO.REL,"\u2266\u0338":e.MO.REL,"\u2267":e.MO.REL,"\u2268":e.MO.REL,"\u2269":e.MO.REL,"\u226a":e.MO.REL,"\u226a\u0338":e.MO.REL,"\u226b":e.MO.REL,"\u226b\u0338":e.MO.REL,"\u226c":e.MO.REL,"\u226d":e.MO.REL,"\u226e":e.MO.REL,"\u226f":e.MO.REL,"\u2270":e.MO.REL,"\u2271":e.MO.REL,"\u2272":e.MO.REL,"\u2273":e.MO.REL,"\u2274":e.MO.REL,"\u2275":e.MO.REL,"\u2276":e.MO.REL,"\u2277":e.MO.REL,"\u2278":e.MO.REL,"\u2279":e.MO.REL,"\u227a":e.MO.REL,"\u227b":e.MO.REL,"\u227c":e.MO.REL,"\u227d":e.MO.REL,"\u227e":e.MO.REL,"\u227f":e.MO.REL,"\u227f\u0338":e.MO.REL,"\u2280":e.MO.REL,"\u2281":e.MO.REL,"\u2282":e.MO.REL,"\u2282\u20d2":e.MO.REL,"\u2283":e.MO.REL,"\u2283\u20d2":e.MO.REL,"\u2284":e.MO.REL,"\u2285":e.MO.REL,"\u2286":e.MO.REL,"\u2287":e.MO.REL,"\u2288":e.MO.REL,"\u2289":e.MO.REL,"\u228a":e.MO.REL,"\u228b":e.MO.REL,"\u228c":e.MO.BIN4,"\u228d":e.MO.BIN4,"\u228e":e.MO.BIN4,"\u228f":e.MO.REL,"\u228f\u0338":e.MO.REL,"\u2290":e.MO.REL,"\u2290\u0338":e.MO.REL,"\u2291":e.MO.REL,"\u2292":e.MO.REL,"\u2293":e.MO.BIN4,"\u2294":e.MO.BIN4,"\u2295":e.MO.BIN4,"\u2296":e.MO.BIN4,"\u2297":e.MO.BIN4,"\u2298":e.MO.BIN4,"\u2299":e.MO.BIN4,"\u229a":e.MO.BIN4,"\u229b":e.MO.BIN4,"\u229c":e.MO.BIN4,"\u229d":e.MO.BIN4,"\u229e":e.MO.BIN4,"\u229f":e.MO.BIN4,"\u22a0":e.MO.BIN4,"\u22a1":e.MO.BIN4,"\u22a2":e.MO.REL,"\u22a3":e.MO.REL,"\u22a4":e.MO.ORD55,"\u22a5":e.MO.REL,"\u22a6":e.MO.REL,"\u22a7":e.MO.REL,"\u22a8":e.MO.REL,"\u22a9":e.MO.REL,"\u22aa":e.MO.REL,"\u22ab":e.MO.REL,"\u22ac":e.MO.REL,"\u22ad":e.MO.REL,"\u22ae":e.MO.REL,"\u22af":e.MO.REL,"\u22b0":e.MO.REL,"\u22b1":e.MO.REL,"\u22b2":e.MO.REL,"\u22b3":e.MO.REL,"\u22b4":e.MO.REL,"\u22b5":e.MO.REL,"\u22b6":e.MO.REL,"\u22b7":e.MO.REL,"\u22b8":e.MO.REL,"\u22b9":e.MO.REL,"\u22ba":e.MO.BIN4,"\u22bb":e.MO.BIN4,"\u22bc":e.MO.BIN4,"\u22bd":e.MO.BIN4,"\u22be":e.MO.BIN3,"\u22bf":e.MO.BIN3,"\u22c4":e.MO.BIN4,"\u22c5":e.MO.BIN4,"\u22c6":e.MO.BIN4,"\u22c7":e.MO.BIN4,"\u22c8":e.MO.REL,"\u22c9":e.MO.BIN4,"\u22ca":e.MO.BIN4,"\u22cb":e.MO.BIN4,"\u22cc":e.MO.BIN4,"\u22cd":e.MO.REL,"\u22ce":e.MO.BIN4,"\u22cf":e.MO.BIN4,"\u22d0":e.MO.REL,"\u22d1":e.MO.REL,"\u22d2":e.MO.BIN4,"\u22d3":e.MO.BIN4,"\u22d4":e.MO.REL,"\u22d5":e.MO.REL,"\u22d6":e.MO.REL,"\u22d7":e.MO.REL,"\u22d8":e.MO.REL,"\u22d9":e.MO.REL,"\u22da":e.MO.REL,"\u22db":e.MO.REL,"\u22dc":e.MO.REL,"\u22dd":e.MO.REL,"\u22de":e.MO.REL,"\u22df":e.MO.REL,"\u22e0":e.MO.REL,"\u22e1":e.MO.REL,"\u22e2":e.MO.REL,"\u22e3":e.MO.REL,"\u22e4":e.MO.REL,"\u22e5":e.MO.REL,"\u22e6":e.MO.REL,"\u22e7":e.MO.REL,"\u22e8":e.MO.REL,"\u22e9":e.MO.REL,"\u22ea":e.MO.REL,"\u22eb":e.MO.REL,"\u22ec":e.MO.REL,"\u22ed":e.MO.REL,"\u22ee":e.MO.ORD55,"\u22ef":e.MO.INNER,"\u22f0":e.MO.REL,"\u22f1":[5,5,i.TEXCLASS.INNER,null],"\u22f2":e.MO.REL,"\u22f3":e.MO.REL,"\u22f4":e.MO.REL,"\u22f5":e.MO.REL,"\u22f6":e.MO.REL,"\u22f7":e.MO.REL,"\u22f8":e.MO.REL,"\u22f9":e.MO.REL,"\u22fa":e.MO.REL,"\u22fb":e.MO.REL,"\u22fc":e.MO.REL,"\u22fd":e.MO.REL,"\u22fe":e.MO.REL,"\u22ff":e.MO.REL,"\u2305":e.MO.BIN3,"\u2306":e.MO.BIN3,"\u2322":e.MO.REL4,"\u2323":e.MO.REL4,"\u2329":e.MO.OPEN,"\u232a":e.MO.CLOSE,"\u23aa":e.MO.ORD,"\u23af":[0,0,i.TEXCLASS.ORD,{stretchy:!0}],"\u23b0":e.MO.OPEN,"\u23b1":e.MO.CLOSE,"\u2500":e.MO.ORD,"\u25b3":e.MO.BIN4,"\u25b5":e.MO.BIN4,"\u25b9":e.MO.BIN4,"\u25bd":e.MO.BIN4,"\u25bf":e.MO.BIN4,"\u25c3":e.MO.BIN4,"\u25ef":e.MO.BIN3,"\u2660":e.MO.ORD,"\u2661":e.MO.ORD,"\u2662":e.MO.ORD,"\u2663":e.MO.ORD,"\u2758":e.MO.REL,"\u27f0":e.MO.RELSTRETCH,"\u27f1":e.MO.RELSTRETCH,"\u27f5":e.MO.WIDEREL,"\u27f6":e.MO.WIDEREL,"\u27f7":e.MO.WIDEREL,"\u27f8":e.MO.WIDEREL,"\u27f9":e.MO.WIDEREL,"\u27fa":e.MO.WIDEREL,"\u27fb":e.MO.WIDEREL,"\u27fc":e.MO.WIDEREL,"\u27fd":e.MO.WIDEREL,"\u27fe":e.MO.WIDEREL,"\u27ff":e.MO.WIDEREL,"\u2900":e.MO.RELACCENT,"\u2901":e.MO.RELACCENT,"\u2902":e.MO.RELACCENT,"\u2903":e.MO.RELACCENT,"\u2904":e.MO.RELACCENT,"\u2905":e.MO.RELACCENT,"\u2906":e.MO.RELACCENT,"\u2907":e.MO.RELACCENT,"\u2908":e.MO.REL,"\u2909":e.MO.REL,"\u290a":e.MO.RELSTRETCH,"\u290b":e.MO.RELSTRETCH,"\u290c":e.MO.WIDEREL,"\u290d":e.MO.WIDEREL,"\u290e":e.MO.WIDEREL,"\u290f":e.MO.WIDEREL,"\u2910":e.MO.WIDEREL,"\u2911":e.MO.RELACCENT,"\u2912":e.MO.RELSTRETCH,"\u2913":e.MO.RELSTRETCH,"\u2914":e.MO.RELACCENT,"\u2915":e.MO.RELACCENT,"\u2916":e.MO.RELACCENT,"\u2917":e.MO.RELACCENT,"\u2918":e.MO.RELACCENT,"\u2919":e.MO.RELACCENT,"\u291a":e.MO.RELACCENT,"\u291b":e.MO.RELACCENT,"\u291c":e.MO.RELACCENT,"\u291d":e.MO.RELACCENT,"\u291e":e.MO.RELACCENT,"\u291f":e.MO.RELACCENT,"\u2920":e.MO.RELACCENT,"\u2921":e.MO.RELSTRETCH,"\u2922":e.MO.RELSTRETCH,"\u2923":e.MO.REL,"\u2924":e.MO.REL,"\u2925":e.MO.REL,"\u2926":e.MO.REL,"\u2927":e.MO.REL,"\u2928":e.MO.REL,"\u2929":e.MO.REL,"\u292a":e.MO.REL,"\u292b":e.MO.REL,"\u292c":e.MO.REL,"\u292d":e.MO.REL,"\u292e":e.MO.REL,"\u292f":e.MO.REL,"\u2930":e.MO.REL,"\u2931":e.MO.REL,"\u2932":e.MO.REL,"\u2933":e.MO.RELACCENT,"\u2934":e.MO.REL,"\u2935":e.MO.REL,"\u2936":e.MO.REL,"\u2937":e.MO.REL,"\u2938":e.MO.REL,"\u2939":e.MO.REL,"\u293a":e.MO.RELACCENT,"\u293b":e.MO.RELACCENT,"\u293c":e.MO.RELACCENT,"\u293d":e.MO.RELACCENT,"\u293e":e.MO.REL,"\u293f":e.MO.REL,"\u2940":e.MO.REL,"\u2941":e.MO.REL,"\u2942":e.MO.RELACCENT,"\u2943":e.MO.RELACCENT,"\u2944":e.MO.RELACCENT,"\u2945":e.MO.RELACCENT,"\u2946":e.MO.RELACCENT,"\u2947":e.MO.RELACCENT,"\u2948":e.MO.RELACCENT,"\u2949":e.MO.REL,"\u294a":e.MO.RELACCENT,"\u294b":e.MO.RELACCENT,"\u294c":e.MO.REL,"\u294d":e.MO.REL,"\u294e":e.MO.WIDEREL,"\u294f":e.MO.RELSTRETCH,"\u2950":e.MO.WIDEREL,"\u2951":e.MO.RELSTRETCH,"\u2952":e.MO.WIDEREL,"\u2953":e.MO.WIDEREL,"\u2954":e.MO.RELSTRETCH,"\u2955":e.MO.RELSTRETCH,"\u2956":e.MO.RELSTRETCH,"\u2957":e.MO.RELSTRETCH,"\u2958":e.MO.RELSTRETCH,"\u2959":e.MO.RELSTRETCH,"\u295a":e.MO.WIDEREL,"\u295b":e.MO.WIDEREL,"\u295c":e.MO.RELSTRETCH,"\u295d":e.MO.RELSTRETCH,"\u295e":e.MO.WIDEREL,"\u295f":e.MO.WIDEREL,"\u2960":e.MO.RELSTRETCH,"\u2961":e.MO.RELSTRETCH,"\u2962":e.MO.RELACCENT,"\u2963":e.MO.REL,"\u2964":e.MO.RELACCENT,"\u2965":e.MO.REL,"\u2966":e.MO.RELACCENT,"\u2967":e.MO.RELACCENT,"\u2968":e.MO.RELACCENT,"\u2969":e.MO.RELACCENT,"\u296a":e.MO.RELACCENT,"\u296b":e.MO.RELACCENT,"\u296c":e.MO.RELACCENT,"\u296d":e.MO.RELACCENT,"\u296e":e.MO.RELSTRETCH,"\u296f":e.MO.RELSTRETCH,"\u2970":e.MO.RELACCENT,"\u2971":e.MO.RELACCENT,"\u2972":e.MO.RELACCENT,"\u2973":e.MO.RELACCENT,"\u2974":e.MO.RELACCENT,"\u2975":e.MO.RELACCENT,"\u2976":e.MO.RELACCENT,"\u2977":e.MO.RELACCENT,"\u2978":e.MO.RELACCENT,"\u2979":e.MO.RELACCENT,"\u297a":e.MO.RELACCENT,"\u297b":e.MO.RELACCENT,"\u297c":e.MO.RELACCENT,"\u297d":e.MO.RELACCENT,"\u297e":e.MO.REL,"\u297f":e.MO.REL,"\u2981":e.MO.BIN3,"\u2982":e.MO.BIN3,"\u2999":e.MO.BIN3,"\u299a":e.MO.BIN3,"\u299b":e.MO.BIN3,"\u299c":e.MO.BIN3,"\u299d":e.MO.BIN3,"\u299e":e.MO.BIN3,"\u299f":e.MO.BIN3,"\u29a0":e.MO.BIN3,"\u29a1":e.MO.BIN3,"\u29a2":e.MO.BIN3,"\u29a3":e.MO.BIN3,"\u29a4":e.MO.BIN3,"\u29a5":e.MO.BIN3,"\u29a6":e.MO.BIN3,"\u29a7":e.MO.BIN3,"\u29a8":e.MO.BIN3,"\u29a9":e.MO.BIN3,"\u29aa":e.MO.BIN3,"\u29ab":e.MO.BIN3,"\u29ac":e.MO.BIN3,"\u29ad":e.MO.BIN3,"\u29ae":e.MO.BIN3,"\u29af":e.MO.BIN3,"\u29b0":e.MO.BIN3,"\u29b1":e.MO.BIN3,"\u29b2":e.MO.BIN3,"\u29b3":e.MO.BIN3,"\u29b4":e.MO.BIN3,"\u29b5":e.MO.BIN3,"\u29b6":e.MO.BIN4,"\u29b7":e.MO.BIN4,"\u29b8":e.MO.BIN4,"\u29b9":e.MO.BIN4,"\u29ba":e.MO.BIN4,"\u29bb":e.MO.BIN4,"\u29bc":e.MO.BIN4,"\u29bd":e.MO.BIN4,"\u29be":e.MO.BIN4,"\u29bf":e.MO.BIN4,"\u29c0":e.MO.REL,"\u29c1":e.MO.REL,"\u29c2":e.MO.BIN3,"\u29c3":e.MO.BIN3,"\u29c4":e.MO.BIN4,"\u29c5":e.MO.BIN4,"\u29c6":e.MO.BIN4,"\u29c7":e.MO.BIN4,"\u29c8":e.MO.BIN4,"\u29c9":e.MO.BIN3,"\u29ca":e.MO.BIN3,"\u29cb":e.MO.BIN3,"\u29cc":e.MO.BIN3,"\u29cd":e.MO.BIN3,"\u29ce":e.MO.REL,"\u29cf":e.MO.REL,"\u29cf\u0338":e.MO.REL,"\u29d0":e.MO.REL,"\u29d0\u0338":e.MO.REL,"\u29d1":e.MO.REL,"\u29d2":e.MO.REL,"\u29d3":e.MO.REL,"\u29d4":e.MO.REL,"\u29d5":e.MO.REL,"\u29d6":e.MO.BIN4,"\u29d7":e.MO.BIN4,"\u29d8":e.MO.BIN3,"\u29d9":e.MO.BIN3,"\u29db":e.MO.BIN3,"\u29dc":e.MO.BIN3,"\u29dd":e.MO.BIN3,"\u29de":e.MO.REL,"\u29df":e.MO.BIN3,"\u29e0":e.MO.BIN3,"\u29e1":e.MO.REL,"\u29e2":e.MO.BIN4,"\u29e3":e.MO.REL,"\u29e4":e.MO.REL,"\u29e5":e.MO.REL,"\u29e6":e.MO.REL,"\u29e7":e.MO.BIN3,"\u29e8":e.MO.BIN3,"\u29e9":e.MO.BIN3,"\u29ea":e.MO.BIN3,"\u29eb":e.MO.BIN3,"\u29ec":e.MO.BIN3,"\u29ed":e.MO.BIN3,"\u29ee":e.MO.BIN3,"\u29ef":e.MO.BIN3,"\u29f0":e.MO.BIN3,"\u29f1":e.MO.BIN3,"\u29f2":e.MO.BIN3,"\u29f3":e.MO.BIN3,"\u29f4":e.MO.REL,"\u29f5":e.MO.BIN4,"\u29f6":e.MO.BIN4,"\u29f7":e.MO.BIN4,"\u29f8":e.MO.BIN3,"\u29f9":e.MO.BIN3,"\u29fa":e.MO.BIN3,"\u29fb":e.MO.BIN3,"\u29fe":e.MO.BIN4,"\u29ff":e.MO.BIN4,"\u2a1d":e.MO.BIN3,"\u2a1e":e.MO.BIN3,"\u2a1f":e.MO.BIN3,"\u2a20":e.MO.BIN3,"\u2a21":e.MO.BIN3,"\u2a22":e.MO.BIN4,"\u2a23":e.MO.BIN4,"\u2a24":e.MO.BIN4,"\u2a25":e.MO.BIN4,"\u2a26":e.MO.BIN4,"\u2a27":e.MO.BIN4,"\u2a28":e.MO.BIN4,"\u2a29":e.MO.BIN4,"\u2a2a":e.MO.BIN4,"\u2a2b":e.MO.BIN4,"\u2a2c":e.MO.BIN4,"\u2a2d":e.MO.BIN4,"\u2a2e":e.MO.BIN4,"\u2a2f":e.MO.BIN4,"\u2a30":e.MO.BIN4,"\u2a31":e.MO.BIN4,"\u2a32":e.MO.BIN4,"\u2a33":e.MO.BIN4,"\u2a34":e.MO.BIN4,"\u2a35":e.MO.BIN4,"\u2a36":e.MO.BIN4,"\u2a37":e.MO.BIN4,"\u2a38":e.MO.BIN4,"\u2a39":e.MO.BIN4,"\u2a3a":e.MO.BIN4,"\u2a3b":e.MO.BIN4,"\u2a3c":e.MO.BIN4,"\u2a3d":e.MO.BIN4,"\u2a3e":e.MO.BIN4,"\u2a3f":e.MO.BIN4,"\u2a40":e.MO.BIN4,"\u2a41":e.MO.BIN4,"\u2a42":e.MO.BIN4,"\u2a43":e.MO.BIN4,"\u2a44":e.MO.BIN4,"\u2a45":e.MO.BIN4,"\u2a46":e.MO.BIN4,"\u2a47":e.MO.BIN4,"\u2a48":e.MO.BIN4,"\u2a49":e.MO.BIN4,"\u2a4a":e.MO.BIN4,"\u2a4b":e.MO.BIN4,"\u2a4c":e.MO.BIN4,"\u2a4d":e.MO.BIN4,"\u2a4e":e.MO.BIN4,"\u2a4f":e.MO.BIN4,"\u2a50":e.MO.BIN4,"\u2a51":e.MO.BIN4,"\u2a52":e.MO.BIN4,"\u2a53":e.MO.BIN4,"\u2a54":e.MO.BIN4,"\u2a55":e.MO.BIN4,"\u2a56":e.MO.BIN4,"\u2a57":e.MO.BIN4,"\u2a58":e.MO.BIN4,"\u2a59":e.MO.REL,"\u2a5a":e.MO.BIN4,"\u2a5b":e.MO.BIN4,"\u2a5c":e.MO.BIN4,"\u2a5d":e.MO.BIN4,"\u2a5e":e.MO.BIN4,"\u2a5f":e.MO.BIN4,"\u2a60":e.MO.BIN4,"\u2a61":e.MO.BIN4,"\u2a62":e.MO.BIN4,"\u2a63":e.MO.BIN4,"\u2a64":e.MO.BIN4,"\u2a65":e.MO.BIN4,"\u2a66":e.MO.REL,"\u2a67":e.MO.REL,"\u2a68":e.MO.REL,"\u2a69":e.MO.REL,"\u2a6a":e.MO.REL,"\u2a6b":e.MO.REL,"\u2a6c":e.MO.REL,"\u2a6d":e.MO.REL,"\u2a6e":e.MO.REL,"\u2a6f":e.MO.REL,"\u2a70":e.MO.REL,"\u2a71":e.MO.BIN4,"\u2a72":e.MO.BIN4,"\u2a73":e.MO.REL,"\u2a74":e.MO.REL,"\u2a75":e.MO.REL,"\u2a76":e.MO.REL,"\u2a77":e.MO.REL,"\u2a78":e.MO.REL,"\u2a79":e.MO.REL,"\u2a7a":e.MO.REL,"\u2a7b":e.MO.REL,"\u2a7c":e.MO.REL,"\u2a7d":e.MO.REL,"\u2a7d\u0338":e.MO.REL,"\u2a7e":e.MO.REL,"\u2a7e\u0338":e.MO.REL,"\u2a7f":e.MO.REL,"\u2a80":e.MO.REL,"\u2a81":e.MO.REL,"\u2a82":e.MO.REL,"\u2a83":e.MO.REL,"\u2a84":e.MO.REL,"\u2a85":e.MO.REL,"\u2a86":e.MO.REL,"\u2a87":e.MO.REL,"\u2a88":e.MO.REL,"\u2a89":e.MO.REL,"\u2a8a":e.MO.REL,"\u2a8b":e.MO.REL,"\u2a8c":e.MO.REL,"\u2a8d":e.MO.REL,"\u2a8e":e.MO.REL,"\u2a8f":e.MO.REL,"\u2a90":e.MO.REL,"\u2a91":e.MO.REL,"\u2a92":e.MO.REL,"\u2a93":e.MO.REL,"\u2a94":e.MO.REL,"\u2a95":e.MO.REL,"\u2a96":e.MO.REL,"\u2a97":e.MO.REL,"\u2a98":e.MO.REL,"\u2a99":e.MO.REL,"\u2a9a":e.MO.REL,"\u2a9b":e.MO.REL,"\u2a9c":e.MO.REL,"\u2a9d":e.MO.REL,"\u2a9e":e.MO.REL,"\u2a9f":e.MO.REL,"\u2aa0":e.MO.REL,"\u2aa1":e.MO.REL,"\u2aa1\u0338":e.MO.REL,"\u2aa2":e.MO.REL,"\u2aa2\u0338":e.MO.REL,"\u2aa3":e.MO.REL,"\u2aa4":e.MO.REL,"\u2aa5":e.MO.REL,"\u2aa6":e.MO.REL,"\u2aa7":e.MO.REL,"\u2aa8":e.MO.REL,"\u2aa9":e.MO.REL,"\u2aaa":e.MO.REL,"\u2aab":e.MO.REL,"\u2aac":e.MO.REL,"\u2aad":e.MO.REL,"\u2aae":e.MO.REL,"\u2aaf":e.MO.REL,"\u2aaf\u0338":e.MO.REL,"\u2ab0":e.MO.REL,"\u2ab0\u0338":e.MO.REL,"\u2ab1":e.MO.REL,"\u2ab2":e.MO.REL,"\u2ab3":e.MO.REL,"\u2ab4":e.MO.REL,"\u2ab5":e.MO.REL,"\u2ab6":e.MO.REL,"\u2ab7":e.MO.REL,"\u2ab8":e.MO.REL,"\u2ab9":e.MO.REL,"\u2aba":e.MO.REL,"\u2abb":e.MO.REL,"\u2abc":e.MO.REL,"\u2abd":e.MO.REL,"\u2abe":e.MO.REL,"\u2abf":e.MO.REL,"\u2ac0":e.MO.REL,"\u2ac1":e.MO.REL,"\u2ac2":e.MO.REL,"\u2ac3":e.MO.REL,"\u2ac4":e.MO.REL,"\u2ac5":e.MO.REL,"\u2ac6":e.MO.REL,"\u2ac7":e.MO.REL,"\u2ac8":e.MO.REL,"\u2ac9":e.MO.REL,"\u2aca":e.MO.REL,"\u2acb":e.MO.REL,"\u2acc":e.MO.REL,"\u2acd":e.MO.REL,"\u2ace":e.MO.REL,"\u2acf":e.MO.REL,"\u2ad0":e.MO.REL,"\u2ad1":e.MO.REL,"\u2ad2":e.MO.REL,"\u2ad3":e.MO.REL,"\u2ad4":e.MO.REL,"\u2ad5":e.MO.REL,"\u2ad6":e.MO.REL,"\u2ad7":e.MO.REL,"\u2ad8":e.MO.REL,"\u2ad9":e.MO.REL,"\u2ada":e.MO.REL,"\u2adb":e.MO.REL,"\u2adc":e.MO.REL,"\u2add":e.MO.REL,"\u2ade":e.MO.REL,"\u2adf":e.MO.REL,"\u2ae0":e.MO.REL,"\u2ae1":e.MO.REL,"\u2ae2":e.MO.REL,"\u2ae3":e.MO.REL,"\u2ae4":e.MO.REL,"\u2ae5":e.MO.REL,"\u2ae6":e.MO.REL,"\u2ae7":e.MO.REL,"\u2ae8":e.MO.REL,"\u2ae9":e.MO.REL,"\u2aea":e.MO.REL,"\u2aeb":e.MO.REL,"\u2aec":e.MO.REL,"\u2aed":e.MO.REL,"\u2aee":e.MO.REL,"\u2aef":e.MO.REL,"\u2af0":e.MO.REL,"\u2af1":e.MO.REL,"\u2af2":e.MO.REL,"\u2af3":e.MO.REL,"\u2af4":e.MO.BIN4,"\u2af5":e.MO.BIN4,"\u2af6":e.MO.BIN4,"\u2af7":e.MO.REL,"\u2af8":e.MO.REL,"\u2af9":e.MO.REL,"\u2afa":e.MO.REL,"\u2afb":e.MO.BIN4,"\u2afd":e.MO.BIN4,"\u2afe":e.MO.BIN3,"\u2b45":e.MO.RELSTRETCH,"\u2b46":e.MO.RELSTRETCH,"\u3008":e.MO.OPEN,"\u3009":e.MO.CLOSE,"\ufe37":e.MO.WIDEACCENT,"\ufe38":e.MO.WIDEACCENT}},e.OPTABLE.infix["^"]=e.MO.WIDEREL,e.OPTABLE.infix._=e.MO.WIDEREL,e.OPTABLE.prefix["\u2223"]=e.MO.OPEN,e.OPTABLE.prefix["\u2225"]=e.MO.OPEN,e.OPTABLE.postfix["\u2223"]=e.MO.CLOSE,e.OPTABLE.postfix["\u2225"]=e.MO.CLOSE},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),o=this&&this.__assign||function(){return(o=Object.assign||function(t){for(var e,r=1,n=arguments.length;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var o,s=r(24),c=(o=s.PrioritizedList,i(l,o),l.prototype.register=function(t){return this.add(t,t.priority)},l.prototype.unregister=function(t){this.remove(t)},l.prototype.handlesDocument=function(t){var e,r;try{for(var n=a(this),i=n.next();!i.done;i=n.next()){var o=i.value.item;if(o.handlesDocument(t))return o}}catch(t){e={error:t}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(e)throw e.error}}throw new Error("Can't find handler for document")},l.prototype.document=function(t,e){return void 0===e&&(e=null),this.handlesDocument(t).create(t,e)},l);function l(){return null!==o&&o.apply(this,arguments)||this}e.HandlerList=c},function(t,e,r){"use strict";var c=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},n=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},s=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0\n",o=e;e+=" ";try{for(var a=l(t.childNodes),s=a.next();!s.done;s=a.next()){var c=s.value;i+=this.visitNode(c,e)}}catch(t){r={error:t}}finally{try{s&&!s.done&&(n=a.return)&&n.call(a)}finally{if(r)throw r.error}}return i+="\n"+o+""},h.prototype.visitAnnotationNode=function(t,e){return e+""+this.childNodeMml(t,"","")+"
"},h.prototype.visitDefault=function(t,e){var r=t.kind,n=s(t.isToken||0===t.childNodes.length?["",""]:["\n",e],2),i=n[0],o=n[1],a=this.childNodeMml(t,e+" ",i);return e+"<"+r+this.getAttributes(t)+">"+(a.match(/\S/)?i+a+o:"")+""},h.prototype.childNodeMml=function(t,e,r){var n,i,o="";try{for(var a=l(t.childNodes),s=a.next();!s.done;s=a.next()){var c=s.value;o+=this.visitNode(c,e)+r}}catch(t){n={error:t}}finally{try{s&&!s.done&&(i=a.return)&&i.call(a)}finally{if(n)throw n.error}}return o},h.prototype.getAttributes=function(t){var e,r,n="",i=t.attributes.getAllAttributes();try{for(var o=l(Object.keys(i)),a=o.next();!a.done;a=o.next()){var s=a.value;void 0!==i[s]&&(n+=" "+s+'="'+this.quoteHTML(i[s].toString())+'"')}}catch(t){e={error:t}}finally{try{a&&!a.done&&(r=o.return)&&r.call(o)}finally{if(e)throw e.error}}return n},h.prototype.quoteHTML=function(t){return t.replace(/&/g,"&").replace(//g,">").replace(/\"/g,""").replace(/([\uD800-\uDBFF].)/g,function(t,e){return"&#x"+(1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320)+65536).toString(16).toUpperCase()+";"}).replace(/([\u0080-\uD7FF\uE000-\uFFFF])/g,function(t,e){return"&#x"+e.charCodeAt(0).toString(16).toUpperCase()+";"})},h);function h(){return null!==o&&o.apply(this,arguments)||this}e.SerializedMmlVisitor=c},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=(Object.defineProperty(i.prototype,"kind",{get:function(){return this.node.kind},enumerable:!0,configurable:!0}),i.prototype.wrap=function(t){return this.factory.wrap(t)},i);function i(t,e){this.factory=t,this.node=e}e.AbstractWrapper=n},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),o=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var s,a=r(41),w=r(3),c=r(100),l=r(101),u=r(102),h=r(13),f=(s=a.AbstractMathDocument,i(p,s),p.prototype.findPosition=function(t,e,r,n){var i,o;try{for(var a=T(n[t]),s=a.next();!s.done;s=a.next()){var c=s.value,l=_(c,2),u=l[0],h=l[1];if(e<=h)return{node:u,n:e,delim:r};e-=h}}catch(t){i={error:t}}finally{try{s&&!s.done&&(o=a.return)&&o.call(a)}finally{if(i)throw i.error}}return{node:null,n:0,delim:r}},p.prototype.mathItem=function(t,e,r){var n=t.math,i=this.findPosition(t.n,t.start.n,t.open,r),o=this.findPosition(t.n,t.end.n,t.close,r);return new this.options.MathItem(n,e,t.display,i,o)},p.prototype.findMath=function(t){var e,r,n,i,o,a,s,c,l;if(!this.processed.isSet("findMath")){this.adaptor.document=this.document,t=w.userOptions({elements:[this.adaptor.body(this.document)]},t);try{for(var u=T(this.adaptor.getElements(t.elements,this.document)),h=u.next();!h.done;h=u.next()){var f=h.value,p=_([null,null],2),d=p[0],m=p[1];try{for(var y=(n=void 0,T(this.inputJax)),v=y.next();!v.done;v=y.next()){var b=v.value,g=new this.options.MathList;if(b.processStrings){null===d&&(d=(o=_(this.domStrings.find(f),2))[0],m=o[1]);try{for(var M=(a=void 0,T(b.findMath(d))),O=M.next();!O.done;O=M.next()){var x=O.value;g.push(this.mathItem(x,b,m))}}catch(t){a={error:t}}finally{try{O&&!O.done&&(s=M.return)&&s.call(M)}finally{if(a)throw a.error}}}else try{for(var S=(c=void 0,T(b.findMath(f))),E=S.next();!E.done;E=S.next()){x=E.value;var C=new this.options.MathItem(x.math,b,x.display,x.start,x.end);g.push(C)}}catch(t){c={error:t}}finally{try{E&&!E.done&&(l=S.return)&&l.call(S)}finally{if(c)throw c.error}}this.math.merge(g)}}catch(t){n={error:t}}finally{try{v&&!v.done&&(i=y.return)&&i.call(y)}finally{if(n)throw n.error}}}}catch(t){e={error:t}}finally{try{h&&!h.done&&(r=u.return)&&r.call(u)}finally{if(e)throw e.error}}this.processed.set("findMath")}return this},p.prototype.updateDocument=function(){return this.processed.isSet("updateDocument")||(this.addPageElements(),this.addStyleSheet(),s.prototype.updateDocument.call(this),this.processed.set("updateDocument")),this},p.prototype.addPageElements=function(){var t=this.adaptor.body(this.document),e=this.documentPageElements();e&&this.adaptor.append(t,e)},p.prototype.addStyleSheet=function(){var t=this.documentStyleSheet();if(t){var e=this.adaptor.head(this.document),r=this.findSheet(e,this.adaptor.getAttribute(t,"id"));r?this.adaptor.replace(t,r):this.adaptor.append(e,t)}},p.prototype.findSheet=function(t,e){var r,n;if(e)try{for(var i=T(this.adaptor.tags(t,"style")),o=i.next();!o.done;o=i.next()){var a=o.value;if(this.adaptor.getAttribute(a,"id")===e)return a}}catch(t){r={error:t}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}return null},p.prototype.removeFromDocument=function(t){var e,r;if(void 0===t&&(t=!1),this.processed.isSet("updateDocument"))try{for(var n=T(this.math),i=n.next();!i.done;i=n.next()){var o=i.value;o.state()>=h.STATE.INSERTED&&o.state(h.STATE.TYPESET,t)}}catch(t){e={error:t}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(e)throw e.error}}return this.processed.clear("updateDocument"),this},p.prototype.documentStyleSheet=function(){return this.outputJax.styleSheet(this)},p.prototype.documentPageElements=function(){return this.outputJax.pageElements(this)},p.KIND="HTML",p.OPTIONS=o(o({},a.AbstractMathDocument.OPTIONS),{renderActions:w.expandable(o(o({},a.AbstractMathDocument.OPTIONS.renderActions),{styles:[h.STATE.INSERTED+1,"","updateStyleSheet",!1]})),MathList:l.HTMLMathList,MathItem:c.HTMLMathItem,DomStrings:null}),p);function p(t,e,r){var n=this,i=_(w.separateOptions(r,u.HTMLDomStrings.OPTIONS),2),o=i[0],a=i[1];return(n=s.call(this,t,e,o)||this).domStrings=n.options.DomStrings||new u.HTMLDomStrings(a),n.domStrings.adaptor=e,n}e.HTMLDocument=f},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(13),s=(o=a.AbstractMathItem,i(c,o),Object.defineProperty(c.prototype,"adaptor",{get:function(){return this.inputJax.adaptor},enumerable:!0,configurable:!0}),c.prototype.updateDocument=function(t){if(this.state()=a.STATE.TYPESET){var e=this.start.node,r=this.adaptor.text("");if(t){var n=this.start.delim+this.math+this.end.delim;if(this.inputJax.processStrings)r=this.adaptor.text(n);else{var i=this.adaptor.parse(n,"text/html");r=this.adaptor.firstChild(this.adaptor.body(i))}}this.adaptor.replace(r,e),this.start.node=this.end.node=r,this.start.n=this.end.n=0}},c);function c(t,e,r,n,i){return void 0===r&&(r=!0),void 0===n&&(n={node:null,n:0,delim:""}),void 0===i&&(i={node:null,n:0,delim:""}),o.call(this,t,e,r,n,i)||this}e.HTMLMathItem=s},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(43),s=(o=a.AbstractMathList,i(c,o),c);function c(){return null!==o&&o.apply(this,arguments)||this}e.HTMLMathList=s},function(t,e,r){"use strict";var s=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var a=r(29),u=r(3),h=r(106),f=r(107),s=r(6),p=r(21),d=r(4),m=r(110),y=r(27),v=r(11);r(113);var b,g=(b=a.AbstractInputJax,i(M,b),M.configure=function(t){var e,r,n=v.Configuration.empty();try{for(var i=c(t),o=i.next();!o.done;o=i.next()){var a=o.value,s=v.ConfigurationHandler.get(a);s&&n.append(s)}}catch(t){e={error:t}}finally{try{o&&!o.done&&(r=i.return)&&r.call(i)}finally{if(e)throw e.error}}return n.init(n),n},M.tags=function(t,e){y.TagsFactory.addTags(e.tags),y.TagsFactory.setDefault(t.options.tags),t.tags=y.TagsFactory.getDefault(),t.tags.configuration=t},M.prototype.setMmlFactory=function(t){b.prototype.setMmlFactory.call(this,t),this._parseOptions.nodeFactory.setMmlFactory(t)},Object.defineProperty(M.prototype,"parseOptions",{get:function(){return this._parseOptions},enumerable:!0,configurable:!0}),M.prototype.compile=function(t,e){this.parseOptions.clear(),this.executeFilters(this.preFilters,t,e,this.parseOptions);var r,n=t.display;this.latex=t.math,this.parseOptions.tags.startEquation(t);try{r=new p.default(this.latex,{display:n,isInner:!1},this.parseOptions).mml()}catch(t){if(!(t instanceof d.default))throw t;this.parseOptions.error=!0,r=this.formatError(t)}return r=this.parseOptions.nodeFactory.create("node","math",[r]),n&&s.default.setAttribute(r,"display","block"),this.parseOptions.tags.finishEquation(t),this.parseOptions.root=r,this.executeFilters(this.postFilters,t,e,this.parseOptions),this.mathNode=this.parseOptions.root,this.mathNode},M.prototype.findMath=function(t){return this.findTeX.findMath(t)},M.prototype.formatError=function(t){var e=t.message.replace(/\n.*/,"");return this.parseOptions.nodeFactory.create("error",e,t.id,this.latex)},M.NAME="TeX",M.OPTIONS=o(o({},a.AbstractInputJax.OPTIONS),{FindTeX:null,packages:["base"],digits:/^(?:[0-9]+(?:\{,\}[0-9]{3})*(?:\.[0-9]*)?|\.[0-9]+)/,maxBuffer:5120}),M);function M(t){void 0===t&&(t={});var e=this,r=l(u.separateOptions(t,M.OPTIONS,h.FindTeX.OPTIONS),3),n=r[0],i=r[1],o=r[2];(e=b.call(this,i)||this).findTeX=e.options.FindTeX||new h.FindTeX(o);var a=e.options.packages,s=e.configuration=M.configure(a),c=e._parseOptions=new m.default(s,[e.options,y.TagsFactory.OPTIONS]);return u.userOptions(c.options,n),s.config(s,e),M.tags(c,s),e.postFilters.add(f.default.cleanSubSup,-5),e.postFilters.add(f.default.setInherited,-4),e.postFilters.add(f.default.cleanStretchy,-3),e.postFilters.add(f.default.cleanAttributes,-2),e.postFilters.add(f.default.combineRelations,-1),e}e.TeX=g},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),h=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var n,d=r(0),m=r(6);!function(t){t.cleanStretchy=function(t){var e,r,n=t.data;try{for(var i=p(n.getList("fixStretchy")),o=i.next();!o.done;o=i.next()){var a=o.value;if(m.default.getProperty(a,"fixStretchy")){var s=m.default.getForm(a);s&&s[3]&&s[3].stretchy&&m.default.setAttribute(a,"stretchy",!1);var c=a.parent;if(!(m.default.getTexClass(a)||s&&s[2])){var l=n.nodeFactory.create("node","TeXAtom",[a]);c.replaceChild(l,a),l.inheritAttributesFrom(a)}m.default.removeProperties(a,"fixStretchy")}}}catch(t){e={error:t}}finally{try{o&&!o.done&&(r=i.return)&&r.call(i)}finally{if(e)throw e.error}}},t.cleanAttributes=function(t){t.data.root.walkTree(function(t,e){var r,n,i=t.attributes;try{for(var o=p(i.getExplicitNames()),a=o.next();!a.done;a=o.next()){var s=a.value;i.attributes[s]===t.attributes.getInherited(s)&&delete i.attributes[s]}}catch(t){r={error:t}}finally{try{a&&!a.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}},{})},t.combineRelations=function(t){var e,r;try{for(var n=p(t.data.getList("mo")),i=n.next();!i.done;i=n.next()){var o=i.value;if(!o.getProperty("relationsCombined")&&o.parent&&(!o.parent||m.default.isType(o.parent,"mrow"))&&m.default.getTexClass(o)===d.TEXCLASS.REL){for(var a=o.parent,s=void 0,c=a.childNodes,l=c.indexOf(o)+1,u=m.default.getProperty(o,"variantForm");l\u20d2",nvinfin:"\u29de",nvlArr:"\u2902",nvle:"\u2264\u20d2",nvlt:"<\u20d2",nvltrie:"\u22b4\u20d2",nvrArr:"\u2903",nvrtrie:"\u22b5\u20d2",nvsim:"\u223c\u20d2",nwArr:"\u21d6",nwarhk:"\u2923",nwarrow:"\u2196",nwnear:"\u2927"},"n")},function(t,e,r){"use strict";var u=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},h=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var o=r(111),a=r(8),s=r(112),l=r(3),u=(h.prototype.pushParser=function(t){this.parsers.unshift(t)},h.prototype.popParser=function(){this.parsers.shift()},Object.defineProperty(h.prototype,"parser",{get:function(){return this.parsers[0]},enumerable:!0,configurable:!0}),h.prototype.clear=function(){this.parsers=[],this.root=null,this.nodeLists={},this.error=!1,this.tags.resetTag()},h.prototype.addNode=function(t,e){var r=this.nodeLists[t];(r=r||(this.nodeLists[t]=[])).push(e)},h.prototype.getList=function(t){var e,r,n=this.nodeLists[t]||[],i=[];try{for(var o=c(n),a=o.next();!a.done;a=o.next()){var s=a.value;this.inTree(s)&&i.push(s)}}catch(t){e={error:t}}finally{try{a&&!a.done&&(r=o.return)&&r.call(o)}finally{if(e)throw e.error}}return this.nodeLists[t]=i},h.prototype.inTree=function(t){for(;t&&t!==this.root;)t=t.parent;return!!t},h);function h(t,e){void 0===e&&(e=[]),this.options={},this.parsers=[],this.root=null,this.nodeLists={},this.error=!1,this.handlers=new a.SubHandlers(t),this.nodeFactory=new s.NodeFactory,(this.nodeFactory.configuration=this).nodeFactory.setCreators(t.nodes),this.itemFactory=new o.default(t.items),this.itemFactory.configuration=this,l.defaultOptions.apply(void 0,i([this.options],e)),l.defaultOptions(this.options,t.options)}e.default=u},function(t,e,r){"use strict";var n,i,o=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0});var a,s=r(32),c=r(30),l=(a=s.BaseItem,o(u,a),u);function u(){return null!==a&&a.apply(this,arguments)||this}var h,f=(h=c.AbstractFactory,o(p,h),p.DefaultStackItems=((i={})[l.prototype.kind]=l,i),p);function p(){var t=null!==h&&h.apply(this,arguments)||this;return t.defaultKind="dummy",t.configuration=null,t}e.default=f},function(t,e,r){"use strict";var n=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},r=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0"),i=this.checkForErrors(this.adaptor.parse(n,"text/"+this.options.parseAs)),o=this.adaptor.body(i);1!==this.adaptor.childNodes(o).length&&this.error("MathML must consist of a single element"),r=this.adaptor.remove(this.adaptor.firstChild(o)),"math"!==this.adaptor.kind(r).replace(/^[a-z]+:/,"")&&this.error("MathML must be formed by a element, not <"+this.adaptor.kind(r)+">")}return r=this.executeFilters(this.mmlFilters,t,e,r),this.executeFilters(this.postFilters,t,e,this.mathml.compile(r))},p.prototype.checkForErrors=function(t){var e=this.adaptor.tags(this.adaptor.body(t),"parsererror")[0];return e&&(""===this.adaptor.textContent(e)&&this.error("Error processing MathML"),this.options.parseError.call(this,e)),t},p.prototype.error=function(t){throw new Error(t)},p.prototype.findMath=function(t){return this.findMathML.findMath(t)},p.NAME="MathML",p.OPTIONS=c.defaultOptions({parseAs:"html",forceReparse:!1,FindMathML:null,MathMLCompile:null,parseError:function(t){this.error(this.adaptor.textContent(t).replace(/\n.*/g,""))}},o.AbstractInputJax.OPTIONS),p);function p(t){void 0===t&&(t={});var e=this,r=a(c.separateOptions(t,u.FindMathML.OPTIONS,h.MathMLCompile.OPTIONS),3),n=r[0],i=r[1],o=r[2];return(e=s.call(this,n)||this).findMathML=e.options.FindMathML||new u.FindMathML(i),e.mathml=e.options.MathMLCompile||new h.MathMLCompile(o),e.mmlFilters=new l.FunctionList,e}e.MathML=f},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),d=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(40),m="http://www.w3.org/1998/Math/MathML",s=(o=a.AbstractFindMath,i(c,o),c.prototype.findMath=function(t){var e=new Set;this.findMathNodes(t,e),this.findMathPrefixed(t,e);var r=this.adaptor.root(this.adaptor.document);return"html"===this.adaptor.kind(r)&&0===e.size&&this.findMathNS(t,e),this.processMath(e)},c.prototype.findMathNodes=function(t,e){var r,n;try{for(var i=d(this.adaptor.tags(t,"math")),o=i.next();!o.done;o=i.next()){var a=o.value;e.add(a)}}catch(t){r={error:t}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}},c.prototype.findMathPrefixed=function(t,e){var r,n,i,o,a=this.adaptor.root(this.adaptor.document);try{for(var s=d(this.adaptor.allAttributes(a)),c=s.next();!c.done;c=s.next()){var l=c.value;if("xmlns:"===l.name.substr(0,6)&&l.value===m){var u=l.name.substr(6);try{for(var h=(i=void 0,d(this.adaptor.tags(t,u+":math"))),f=h.next();!f.done;f=h.next()){var p=f.value;e.add(p)}}catch(t){i={error:t}}finally{try{f&&!f.done&&(o=h.return)&&o.call(h)}finally{if(i)throw i.error}}}}}catch(t){r={error:t}}finally{try{c&&!c.done&&(n=s.return)&&n.call(s)}finally{if(r)throw r.error}}},c.prototype.findMathNS=function(t,e){var r,n;try{for(var i=d(this.adaptor.tags(t,"math",m)),o=i.next();!o.done;o=i.next()){var a=o.value;e.add(a)}}catch(t){r={error:t}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}},c.prototype.processMath=function(t){var e,r,n=[];try{for(var i=d(Array.from(t)),o=i.next();!o.done;o=i.next()){var a=o.value,s="block"===this.adaptor.getAttribute(a,"display")||"display"===this.adaptor.getAttribute(a,"mode"),c={node:a,n:0,delim:""},l={node:a,n:0,delim:""};n.push({math:this.adaptor.outerHTML(a),start:c,end:l,display:s})}}catch(t){e={error:t}}finally{try{o&&!o.done&&(r=i.return)&&r.call(i)}finally{if(e)throw e.error}}return n},c.OPTIONS={},c);function c(){return null!==o&&o.apply(this,arguments)||this}e.FindMathML=s},function(t,e,r){"use strict";var n=this&&this.__assign||function(){return(n=Object.assign||function(t){for(var e,r=1,n=arguments.length;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var i=r(0),o=r(3),a=r(12),s=(c.prototype.setMmlFactory=function(t){this.factory=t},c.prototype.compile=function(t){var e=this.makeNode(t);return e.verifyTree(this.options.verify),e.setInheritedAttributes({},!1,0,!1),e.walkTree(this.markMrows),e},c.prototype.makeNode=function(t){var e,r,n=!1,i="",o=this.adaptor.kind(t).replace(/^.*:/,"");try{for(var a=u(this.adaptor.allClasses(t)),s=a.next();!s.done;s=a.next()){var c=s.value;c.match(/^MJX-TeXAtom-/)?(i=c.substr(12),o="TeXAtom"):"MJX-fixedlimits"===c&&(n=!0)}}catch(t){e={error:t}}finally{try{s&&!s.done&&(r=a.return)&&r.call(a)}finally{if(e)throw e.error}}this.factory.getNodeClass(o)||this.error('Unknown node type "'+o+'"');var l=this.factory.create(o);return i&&this.texAtom(l,i,n),this.addAttributes(l,t),this.checkClass(l,t),this.addChildren(l,t),l},c.prototype.addAttributes=function(t,e){var r,n;try{for(var i=u(this.adaptor.allAttributes(e)),o=i.next();!o.done;o=i.next()){var a=o.value,s=a.name;if("class"!==s){var c=this.filterAttribute(s,a.value);if(null!==c){var l=c.toLowerCase();"true"===l||"false"===l?t.attributes.set(s,"true"===l):t.attributes.set(s,c)}}}}catch(t){r={error:t}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}},c.prototype.filterAttribute=function(t,e){return e},c.prototype.addChildren=function(t,e){var r,n;if(0!==t.arity)try{for(var i=u(this.adaptor.childNodes(e)),o=i.next();!o.done;o=i.next()){var a=o.value,s=this.adaptor.kind(a);if("#comment"!==s)if("#text"===s)this.addText(t,a);else if(t.isKind("annotation-xml"))t.appendChild(this.factory.create("XML").setXML(a));else{var c=t.appendChild(this.makeNode(a));0===c.arity&&this.adaptor.childNodes(a).length&&(this.options.fixMisplacedChildren?this.addChildren(t,a):c.mError("There should not be children for "+c.kind+" nodes",this.options.verify,!0))}}}catch(t){r={error:t}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}},c.prototype.addText=function(t,e){var r=this.adaptor.value(e);(t.isToken||t.getProperty("isChars"))&&t.arity?(t.isToken&&(r=a.translate(r),r=this.trimSpace(r)),t.appendChild(this.factory.create("text").setText(r))):r.match(/\S/)&&this.error('Unexpected text node "'+r+'"')},c.prototype.checkClass=function(t,e){var r,n,i=[];try{for(var o=u(this.adaptor.allClasses(e)),a=o.next();!a.done;a=o.next()){var s=a.value;"MJX-"===s.substr(0,4)?"MJX-variant"===s?t.setProperty("variantForm",!0):"MJX-TeXAtom"!==s.substr(0,11)&&t.attributes.set("mathvariant",s.substr(3)):i.push(s)}}catch(t){r={error:t}}finally{try{a&&!a.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}i.length&&t.attributes.set("class",i.join(" "))},c.prototype.texAtom=function(t,e,r){t.texClass=i.TEXCLASS[e],"OP"!==e||r||(t.setProperty("movesupsub",!0),t.attributes.setInherited("movablelimits",!0))},c.prototype.markMrows=function(t){if(t.isKind("mrow")&&!t.isInferred&&2<=t.childNodes.length){var e=t.childNodes[0],r=t.childNodes[t.childNodes.length-1];e.isKind("mo")&&e.attributes.get("fence")&&r.isKind("mo")&&r.attributes.get("fence")&&(e.childNodes.length&&t.setProperty("open",e.getText()),r.childNodes.length&&t.setProperty("close",r.getText()))}},c.prototype.trimSpace=function(t){return t.replace(/[\t\n\r]/g," ").trim().replace(/ +/g," ")},c.prototype.error=function(t){throw new Error(t)},c.OPTIONS={MmlFactory:null,fixMisplacedChildren:!0,verify:{},translateEntities:!0},c.VERIFY=n({},i.AbstractMmlNode.verifyDefaults),c);function c(t){void 0===t&&(t={});var e=this.constructor;this.options=o.userOptions(o.defaultOptions({},e.OPTIONS),t),this.options.verify&&(this.options.verify=o.userOptions(o.defaultOptions({},e.VERIFY),this.options.verify))}e.MathMLCompile=s},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),o=this&&this.__assign||function(){return(o=Object.assign||function(t){for(var e,r=1,n=arguments.length;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var s,c=r(123),l=r(125),u=r(77),h=(s=c.CommonOutputJax,i(f,s),f.prototype.escaped=function(t,e){return this.setDocument(e),this.html("span",{},[this.text(t.math)])},f.prototype.styleSheet=function(t){var e=s.prototype.styleSheet.call(this,t);return this.adaptor.setAttribute(e,"id",f.STYLESHEETID),e},f.prototype.addClassStyles=function(t){var e;this.options.adaptiveCSS&&!t.used||(t.autoStyle&&"unknown"!==t.kind&&this.cssStyles.addStyles(((e={})["mjx-"+t.kind]={display:"inline-block","text-align":"left"},e)),s.prototype.addClassStyles.call(this,t))},f.prototype.processMath=function(t,e){this.factory.wrap(t).toCHTML(e)},f.prototype.clearCache=function(){var e,t;this.cssStyles.clear(),this.font.clearCache();try{for(var r=a(this.factory.getKinds()),n=r.next();!n.done;n=r.next()){var i=n.value;this.factory.getNodeClass(i).used=!1}}catch(t){e={error:t}}finally{try{n&&!n.done&&(t=r.return)&&t.call(r)}finally{if(e)throw e.error}}},f.prototype.unknownText=function(t,e){var r={},n=100/this.math.metrics.scale;return 100!=n&&(r["font-size"]=this.fixed(n,1)+"%"),"-explicitFont"!==e&&this.cssFontStyles(this.font.getCssFont(e),r),this.html("mjx-utext",{variant:e,style:r},[this.text(t)])},f.prototype.measureTextNode=function(t){var e=this.adaptor;t=e.clone(t);var r=this.html("mjx-measure-text",{},[t]);e.append(e.parent(this.math.start.node),this.container),e.append(this.container,r);var n=e.nodeSize(t,this.math.metrics.em)[0]/this.math.metrics.scale;return e.remove(this.container),e.remove(r),{w:n,h:.75,d:.25}},f.prototype.getFontData=function(t){var e=s.prototype.getFontData.call(this,t);return e[0]="MJXZERO, "+e[0],e},f.NAME="CHTML",f.OPTIONS=o(o({},c.CommonOutputJax.OPTIONS),{adaptiveCSS:!0}),f.commonStyles={'mjx-container [space="1"]':{"margin-left":".111em"},'mjx-container [space="2"]':{"margin-left":".167em"},'mjx-container [space="3"]':{"margin-left":".222em"},'mjx-container [space="4"]':{"margin-left":".278em"},'mjx-container [space="5"]':{"margin-left":".333em"},'mjx-container [rspace="1"]':{"margin-right":".111em"},'mjx-container [rspace="2"]':{"margin-right":".167em"},'mjx-container [rspace="3"]':{"margin-right":".222em"},'mjx-container [rspace="4"]':{"margin-right":".278em"},'mjx-container [rspace="5"]':{"margin-right":".333em"},'mjx-container [size="s"]':{"font-size":"70.7%"},'mjx-container [size="ss"]':{"font-size":"50%"},'mjx-container [size="Tn"]':{"font-size":"60%"},'mjx-container [size="sm"]':{"font-size":"85%"},'mjx-container [size="lg"]':{"font-size":"120%"},'mjx-container [size="Lg"]':{"font-size":"144%"},'mjx-container [size="LG"]':{"font-size":"173%"},'mjx-container [size="hg"]':{"font-size":"207%"},'mjx-container [size="HG"]':{"font-size":"249%"},'mjx-container [width="full"]':{width:"100%"},"mjx-box":{display:"inline-block"},"mjx-block":{display:"block"},"mjx-itable":{display:"inline-table"},"mjx-row":{display:"table-row"},"mjx-row > *":{display:"table-cell"},"mjx-mtext":{display:"inline-block"},"mjx-mstyle":{display:"inline-block"},"mjx-merror":{display:"inline-block",color:"red","background-color":"yellow"},"mjx-mphantom":{visibility:"hidden"}},f.STYLESHEETID="MJX-CHTML-styles",f);function f(t){void 0===t&&(t=null);var e=s.call(this,t,l.CHTMLWrapperFactory,u.TeXFont)||this;return e.font.adaptiveCSS(e.options.adaptiveCSS),e}e.CHTML=h},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),o=this&&this.__assign||function(){return(o=Object.assign||function(t){for(var e,r=1,n=arguments.length;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var c,a=r(42),l=r(3),u=r(124),h=r(14),f=r(71),p=(c=a.AbstractOutputJax,i(d,c),d.prototype.typeset=function(t,e){this.setDocument(e);var r=this.createNode();return this.toDOM(t,r,e),r},d.prototype.createNode=function(){var t=this.constructor.NAME;return this.html("mjx-container",{class:"MathJax",jax:t})},d.prototype.setScale=function(t){var e=this.math.metrics.scale*this.options.scale;1!=e&&this.adaptor.setStyle(t,"fontSize",h.percent(e))},d.prototype.toDOM=function(t,e,r){void 0===r&&(r=null),this.setDocument(r),this.math=t,this.pxPerEm=t.metrics.ex/this.font.params.x_height,t.root.setTeXclass(null),this.setScale(e),this.nodeMap=new Map,this.container=e,this.processMath(t.root,e),this.nodeMap=null,this.executeFilters(this.postFilters,t,r,e)},d.prototype.getBBox=function(t,e){this.setDocument(e),(this.math=t).root.setTeXclass(null),this.nodeMap=new Map;var r=this.factory.wrap(t.root).getBBox();return this.nodeMap=null,r},d.prototype.getMetrics=function(t){var e,r;this.setDocument(t);var n=this.adaptor,i=this.getMetricMaps(t);try{for(var o=w(t.math),a=o.next();!a.done;a=o.next()){var s=a.value,c=i[s.display?1:0].get(n.parent(s.start.node)),l=c.em,u=c.ex,h=c.containerWidth,f=c.lineWidth,p=c.scale;s.setMetrics(l,u,h,f,p)}}catch(t){e={error:t}}finally{try{a&&!a.done&&(r=o.return)&&r.call(o)}finally{if(e)throw e.error}}},d.prototype.getMetricsFor=function(t,e){var r=this.getTestElement(t,e),n=this.measureMetrics(r);return this.adaptor.remove(r),n},d.prototype.getMetricMaps=function(t){var e,r,n,i,o,a,s,c,l,u,h=this.adaptor,f=[new Map,new Map];try{for(var p=w(t.math),d=p.next();!d.done;d=p.next()){var m=d.value,y=h.parent(m.start.node),v=f[m.display?1:0];v.has(y)||v.set(y,this.getTestElement(y,m.display))}}catch(t){e={error:t}}finally{try{d&&!d.done&&(r=p.return)&&r.call(p)}finally{if(e)throw e.error}}var b=[new Map,new Map];try{for(var g=w(b.keys()),M=g.next();!M.done;M=g.next()){var O=M.value;try{for(var x=(o=void 0,w(f[O].keys())),S=x.next();!S.done;S=x.next())y=S.value,b[O].set(y,this.measureMetrics(f[O].get(y)))}catch(t){o={error:t}}finally{try{S&&!S.done&&(a=x.return)&&a.call(x)}finally{if(o)throw o.error}}}}catch(t){n={error:t}}finally{try{M&&!M.done&&(i=g.return)&&i.call(g)}finally{if(n)throw n.error}}try{for(var E=w(b.keys()),C=E.next();!C.done;C=E.next()){O=C.value;try{for(var _=(l=void 0,w(f[O].values())),T=_.next();!T.done;T=_.next())y=T.value,h.remove(y)}catch(t){l={error:t}}finally{try{T&&!T.done&&(u=_.return)&&u.call(_)}finally{if(l)throw l.error}}}}catch(t){s={error:t}}finally{try{C&&!C.done&&(c=E.return)&&c.call(E)}finally{if(s)throw s.error}}return b},d.prototype.getTestElement=function(t,e){var r=this.adaptor;if(!this.testInline){this.testInline=this.html("mjx-test",{style:{display:"inline-block",width:"100%","font-style":"normal","font-weight":"normal","font-size":"100%","font-size-adjust":"none","text-indent":0,"text-transform":"none","letter-spacing":"normal","word-spacing":"normal",overflow:"hidden",height:"1px","margin-right":"-1px"}},[this.html("mjx-left-box",{style:{display:"inline-block",width:0,float:"left"}}),this.html("mjx-ex-box",{style:{position:"absolute",overflow:"hidden",width:"1px",height:"60ex"}}),this.html("mjx-right-box",{style:{display:"inline-block",width:0,float:"right"}})]),this.testDisplay=r.clone(this.testInline),r.setStyle(this.testDisplay,"display","table"),r.setStyle(this.testDisplay,"margin-right",""),r.setStyle(r.firstChild(this.testDisplay),"display","none");var n=r.lastChild(this.testDisplay);r.setStyle(n,"display","table-cell"),r.setStyle(n,"width","10000em"),r.setStyle(n,"float","")}return r.append(t,r.clone(e?this.testDisplay:this.testInline))},d.prototype.measureMetrics=function(t){var e=this.adaptor,r=e.fontSize(t),n=e.nodeSize(e.childNode(t,1))[1]/60||r*this.options.exFactor;return{em:r,ex:n,containerWidth:"table"===e.getStyle(t,"display")?e.nodeSize(e.lastChild(t))[0]-1:e.nodeBBox(e.lastChild(t)).left-e.nodeBBox(e.firstChild(t)).left-2,lineWidth:1e6,scale:Math.max(this.options.minScale,this.options.matchFontHeight?n/this.font.params.x_height/r:1)}},d.prototype.styleSheet=function(t){var e,r;this.setDocument(t),this.cssStyles.clear(),this.cssStyles.addStyles(this.constructor.commonStyles);try{for(var n=w(this.factory.getKinds()),i=n.next();!i.done;i=n.next()){var o=i.value;this.addClassStyles(this.factory.getNodeClass(o))}}catch(t){e={error:t}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(e)throw e.error}}return this.cssStyles.addStyles(this.font.styles),this.html("style",{id:"MJX-styles"},[this.text("\n"+this.cssStyles.cssText+"\n")])},d.prototype.addClassStyles=function(t){this.cssStyles.addStyles(t.styles)},d.prototype.setDocument=function(t){t&&(this.document=t,this.adaptor.document=t.document)},d.prototype.html=function(t,e,r,n){return void 0===e&&(e={}),void 0===r&&(r=[]),this.adaptor.node(t,e,r,n)},d.prototype.text=function(t){return this.adaptor.text(t)},d.prototype.fixed=function(t,e){return void 0===e&&(e=3),Math.abs(t)<6e-4?"0":t.toFixed(e).replace(/\.?0+$/,"")},d.prototype.measureText=function(t,e,r){void 0===r&&(r=["",!1,!1]);var n=this.unknownText(t,e);if("-explicitFont"===e){var i=this.cssFontStyles(r);this.adaptor.setAttributes(n,{style:i})}return this.measureTextNodeWithCache(n,t,e,r)},d.prototype.measureTextNodeWithCache=function(t,e,r,n){void 0===n&&(n=["",!1,!1]),"-explicitFont"===r&&(r=[n[0],n[1]?"T":"F",n[2]?"T":"F",""].join("-")),this.unknownCache.has(r)||this.unknownCache.set(r,new Map);var i=this.unknownCache.get(r),o=i.get(e);if(o)return o;var a=this.measureTextNode(t);return i.set(e,a),a},d.prototype.cssFontStyles=function(t,e){void 0===e&&(e={});var r=s(t,3),n=r[0],i=r[1],o=r[2];return e["font-family"]=n,i&&(e["font-style"]="italic"),o&&(e["font-weight"]="bold"),e},d.prototype.getFontData=function(t){return[(t=t||new f.Styles).get("font-family"),"italic"===t.get("font-style"),"bold"===t.get("font-weight")]},d.NAME="Common",d.OPTIONS=o(o({},a.AbstractOutputJax.OPTIONS),{scale:1,minScale:.5,matchFontHeight:!0,mtextInheritFont:!1,merrorInheritFont:!0,mathmlSpacing:!1,skipAttributes:{},exFactor:.5,displayAlign:"center",displayIndent:"0",wrapperFactory:null,font:null,cssStyles:null}),d.commonStyles={},d);function d(t,e,r){void 0===t&&(t=null),void 0===e&&(e=null),void 0===r&&(r=null);var n=this,i=s(l.separateOptions(t,r.OPTIONS),2),o=i[0],a=i[1];return(n=c.call(this,o)||this).factory=n.options.wrapperFactory||new e,(n.factory.jax=n).cssStyles=n.options.cssStyles||new u.CssStyles,n.font=n.options.font||new r(a),n.unknownCache=new Map,n}e.CommonOutputJax=p},function(t,e,r){"use strict";var l=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var n=(Object.defineProperty(i.prototype,"cssText",{get:function(){return this.getStyleString()},enumerable:!0,configurable:!0}),i.prototype.addStyles=function(t){var e,r;if(t)try{for(var n=l(Object.keys(t)),i=n.next();!i.done;i=n.next()){var o=i.value;this.styles[o]||(this.styles[o]={}),Object.assign(this.styles[o],t[o])}}catch(t){e={error:t}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(e)throw e.error}}},i.prototype.removeStyles=function(){for(var e,t,r=[],n=0;n=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var f,o=r(2),d=r(135),a=r(26),p=r(129),s=(f=d.CommonMoMixin(o.CHTMLWrapper),i(c,f),c.prototype.toCHTML=function(t){var e,r,n=this.node.attributes,i=n.get("symmetric")&&2!==this.stretch.dir,o=0!==this.stretch.dir;o&&null===this.size&&this.getStretchedVariant([]);var a=this.standardCHTMLnode(t);if(this.noIC&&this.adaptor.setAttribute(a,"noIC","true"),o&&this.size<0)this.stretchHTML(a,i);else{if(i||n.get("largeop")){var s=p.BBox.empty();f.prototype.computeBBox.call(this,s);var c=this.em((s.d-s.h)/2+this.font.params.axis_height);"0"!==c&&this.adaptor.setStyle(a,"verticalAlign",c)}try{for(var l=h(this.childNodes),u=l.next();!u.done;u=l.next())u.value.toCHTML(a)}catch(t){e={error:t}}finally{try{u&&!u.done&&(r=l.return)&&r.call(l)}finally{if(e)throw e.error}}}},c.prototype.stretchHTML=function(t,e){var r=this.getText().charCodeAt(0),n=this.stretch;n.used=!0;var i=n.stretch,o=[];i[0]&&o.push(this.html("mjx-beg",{},[this.html("mjx-c")])),o.push(this.html("mjx-ext",{},[this.html("mjx-c")])),4===i.length&&o.push(this.html("mjx-mid",{},[this.html("mjx-c")]),this.html("mjx-ext",{},[this.html("mjx-c")])),i[2]&&o.push(this.html("mjx-end",{},[this.html("mjx-c")]));var a={},s=this.bbox,c=s.h,l=s.d,u=s.w;1===n.dir?(o.push(this.html("mjx-mark")),a.height=this.em(c+l),a.verticalAlign=this.em(-l)):a.width=this.em(u);var h=d.DirectionVH[n.dir],f={class:this.char(n.c||r),style:a},p=this.html("mjx-stretchy-"+h,f,o);this.adaptor.append(t,p)},c.kind=a.MmlMo.prototype.kind,c.styles={"mjx-stretchy-h":{display:"inline-table",width:"100%"},"mjx-stretchy-h > *":{display:"table-cell",width:0},"mjx-stretchy-h > * > mjx-c":{display:"inline-block"},"mjx-stretchy-h > * > mjx-c::before":{padding:".001em 0",width:"initial"},"mjx-stretchy-h > mjx-ext":{overflow:"hidden",width:"100%"},"mjx-stretchy-h > mjx-ext > mjx-c::before":{transform:"scalex(500)"},"mjx-stretchy-h > mjx-ext > mjx-c":{width:0},"mjx-stretchy-h > mjx-beg > mjx-c":{"margin-right":"-.1em"},"mjx-stretchy-h > mjx-end > mjx-c":{"margin-left":"-.1em"},"mjx-stretchy-v":{display:"inline-block"},"mjx-stretchy-v > *":{display:"block"},"mjx-stretchy-v > mjx-beg":{height:0},"mjx-stretchy-v > mjx-end > mjx-c":{display:"block"},"mjx-stretchy-v > * > mjx-c":{transform:"scale(1)","transform-origin":"left center",overflow:"hidden"},"mjx-stretchy-v > mjx-ext":{display:"block",height:"100%","box-sizing":"border-box",border:"0px solid transparent",overflow:"hidden"},"mjx-stretchy-v > mjx-ext > mjx-c::before":{width:"initial"},"mjx-stretchy-v > mjx-ext > mjx-c":{transform:"scaleY(500) translateY(.1em)",overflow:"visible"},"mjx-mark":{display:"inline-block",height:"0px"}},c);function c(){return null!==f&&f.apply(this,arguments)||this}e.CHTMLmo=s},function(t,e,r){"use strict";var n,i,o=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),m=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var s=r(23);e.DirectionVH=((i={})[1]="v",i[2]="h",i),e.CommonMoMixin=function(t){return o(e,i=t),e.prototype.computeBBox=function(t,e){void 0===e&&(e=!1);var r=0!==this.stretch.dir;if(r&&null===this.size&&this.getStretchedVariant([0]),!(r&&this.size<0)&&(i.prototype.computeBBox.call(this,t),this.copySkewIC(t),this.noIC&&(t.w-=t.ic),this.node.attributes.get("symmetric")&&2!==this.stretch.dir)){var n=(t.h+t.d)/2+this.font.params.axis_height-t.h;t.h+=n,t.d-=n}},e.prototype.getVariant=function(){this.node.attributes.get("largeop")?this.variant=this.node.attributes.get("displaystyle")?"-largeop":"-smallop":i.prototype.getVariant.call(this)},e.prototype.canStretch=function(t){if(0!==this.stretch.dir)return this.stretch.dir===t;if(!this.node.attributes.get("stretchy"))return!1;var e=this.getText();if(1!==e.length)return!1;var r=this.font.getDelimiter(e.charCodeAt(0));return this.stretch=r&&r.dir===t?r:s.NOSTRETCH,0!==this.stretch.dir},e.prototype.getStretchedVariant=function(t,e){var r,n;if(void 0===e&&(e=!1),0!==this.stretch.dir){var i=this.getWH(t),o=this.getSize("minsize",0),a=this.getSize("maxsize",1/0);i=Math.max(o,Math.min(a,i));var s=o||e?i:Math.max(i*this.font.params.delimiterfactor/1e3,i-this.font.params.delimitershortfall),c=this.stretch,l=c.c||this.getText().charCodeAt(0),u=0;if(c.sizes)try{for(var h=p(c.sizes),f=h.next();!f.done;f=h.next()){if(s<=f.value)return this.variant=this.font.getSizeVariant(l,u),void(this.size=u);u++}}catch(t){r={error:t}}finally{try{f&&!f.done&&(n=h.return)&&n.call(h)}finally{if(r)throw r.error}}c.stretch?(this.size=-1,this.invalidateBBox(),this.getStretchBBox(t,i,c)):(this.variant=this.font.getSizeVariant(l,u-1),this.size=u-1)}},e.prototype.getSize=function(t,e){var r=this.node.attributes;return r.isSet(t)&&(e=this.length2em(r.get(t),1,1)),e},e.prototype.getWH=function(t){if(0===t.length)return 0;if(1===t.length)return t[0];var e=m(t,2),r=e[0],n=e[1],i=this.font.params.axis_height;return this.node.attributes.get("symmetric")?2*Math.max(r-i,n+i):r+n},e.prototype.getStretchBBox=function(t,e,r){var n;r.hasOwnProperty("min")&&r.min>e&&(e=r.min);var i=m(r.HDW,3),o=i[0],a=i[1],s=i[2];1===this.stretch.dir?(o=(n=m(this.getBaseline(t,e,r),2))[0],a=n[1]):s=e,this.bbox.h=o,this.bbox.d=a,this.bbox.w=s},e.prototype.getBaseline=function(t,e,r){var n=2===t.length&&t[0]+t[1]===e,i=this.node.attributes.get("symmetric"),o=m(n?t:[e,0],2),a=o[0],s=o[1],c=m([a+s,0],2),l=c[0],u=c[1];if(i){var h=this.font.params.axis_height;n&&(l=2*Math.max(a-h,s+h)),u=l/2-h}else if(n)u=s;else{var f=m(r.HDW||[.75,.25],2),p=f[0],d=f[1];u=d*(l/(p+d))}return[l-u,u]},e.prototype.remapChars=function(t){if(1==t.length){var e=this.node.parent,r=this.isAccent&&(e===this.node.coreParent()||e.isEmbellished)?"accent":"mo",n=this.font.getRemappedChar(r,t[0]);n&&(t=this.unicodeChars(n))}return t},e;function e(){for(var t=[],e=0;e=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(2),s=r(145),c=r(56),l=(o=s.CommonMpaddedMixin(a.CHTMLWrapper),i(u,o),u.prototype.toCHTML=function(t){var e,r,n=this.standardCHTMLnode(t),i=[],o={},a=v(this.getDimens(),9),s=(a[0],a[1],a[2]),c=a[3],l=a[4],u=a[5],h=a[6],f=a[7],p=a[8];if(u&&(o.width=this.em(s+u)),(c||l)&&(o.margin=this.em(c)+" 0 "+this.em(l)),h+p||f){o.position="relative";var d=this.html("mjx-rbox",{style:{left:this.em(h+p),top:this.em(-f)}});h+p&&this.childNodes[0].getBBox().pwidth&&(this.adaptor.setAttribute(d,"width","full"),this.adaptor.setStyle(d,"left",this.em(h))),i.push(d)}n=this.adaptor.append(n,this.html("mjx-block",{style:o},i));try{for(var m=b(this.childNodes),y=m.next();!y.done;y=m.next())y.value.toCHTML(i[0]||n)}catch(t){e={error:t}}finally{try{y&&!y.done&&(r=m.return)&&r.call(m)}finally{if(e)throw e.error}}},u.kind=c.MmlMpadded.prototype.kind,u.styles={"mjx-mpadded":{display:"inline-block"},"mjx-rbox":{display:"inline-block",position:"relative"}},u);function u(){return null!==o&&o.apply(this,arguments)||this}e.CHTMLmpadded=l},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),l=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},m=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0 mjx-dstrike":{display:"inline-block",left:0,top:0,position:"absolute","border-top":b.SOLID,"transform-origin":"top left"},"mjx-menclose > mjx-ustrike":{display:"inline-block",left:0,bottom:0,position:"absolute","border-top":b.SOLID,"transform-origin":"bottom left"},"mjx-menclose > mjx-hstrike":{"border-top":b.SOLID,position:"absolute",left:0,right:0,bottom:"50%",transform:"translateY("+c.em(b.THICKNESS/2)+")"},"mjx-menclose > mjx-vstrike":{"border-left":b.SOLID,position:"absolute",top:0,bottom:0,right:"50%",transform:"translateX("+c.em(b.THICKNESS/2)+")"},"mjx-menclose > mjx-rbox":{position:"absolute",top:0,bottom:0,right:0,left:0,border:b.SOLID,"border-radius":c.em(b.THICKNESS+b.PADDING)},"mjx-menclose > mjx-cbox":{position:"absolute",top:0,bottom:0,right:0,left:0,border:b.SOLID,"border-radius":"50%"},"mjx-menclose > mjx-arrow":{position:"absolute",left:0,bottom:"50%",height:0,width:0},"mjx-menclose > mjx-arrow > *":{display:"block",position:"absolute","transform-origin":"bottom","border-left":c.em(b.THICKNESS*b.ARROWX)+" solid","border-right":0,"box-sizing":"border-box"},"mjx-menclose > mjx-arrow > mjx-aline":{left:0,top:c.em(-b.THICKNESS/2),right:c.em(b.THICKNESS*(b.ARROWX-1)),height:0,"border-top":c.em(b.THICKNESS)+" solid","border-left":0},"mjx-menclose > mjx-arrow[double] > mjx-aline":{left:c.em(b.THICKNESS*(b.ARROWX-1)),height:0},"mjx-menclose > mjx-arrow > mjx-rthead":{transform:"skewX("+u+"rad)",right:0,bottom:"-1px","border-bottom":"1px solid transparent","border-top":c.em(b.THICKNESS*b.ARROWY)+" solid transparent"},"mjx-menclose > mjx-arrow > mjx-rbhead":{transform:"skewX(-"+u+"rad)","transform-origin":"top",right:0,top:"-1px","border-top":"1px solid transparent","border-bottom":c.em(b.THICKNESS*b.ARROWY)+" solid transparent"},"mjx-menclose > mjx-arrow > mjx-lthead":{transform:"skewX(-"+u+"rad)",left:0,bottom:"-1px","border-left":0,"border-right":c.em(b.THICKNESS*b.ARROWX)+" solid","border-bottom":"1px solid transparent","border-top":c.em(b.THICKNESS*b.ARROWY)+" solid transparent"},"mjx-menclose > mjx-arrow > mjx-lbhead":{transform:"skewX("+u+"rad)","transform-origin":"top",left:0,top:"-1px","border-left":0,"border-right":c.em(b.THICKNESS*b.ARROWX)+" solid","border-top":"1px solid transparent","border-bottom":c.em(b.THICKNESS*b.ARROWY)+" solid transparent"},"mjx-menclose > dbox":{position:"absolute",top:0,bottom:0,left:c.em(-1.5*b.PADDING),width:c.em(3*b.PADDING),border:c.em(b.THICKNESS)+" solid","border-radius":"50%","clip-path":"inset(0 0 0 "+c.em(1.5*b.PADDING)+")","box-sizing":"border-box"}},f.notations=new Map([b.Border("top"),b.Border("right"),b.Border("bottom"),b.Border("left"),b.Border2("actuarial","top","right"),b.Border2("madruwb","bottom","right"),b.DiagonalStrike("up",1),b.DiagonalStrike("down",-1),["horizontalstrike",{renderer:b.RenderElement("hstrike","Y"),bbox:function(t){return[0,t.padding,0,t.padding]}}],["verticalstrike",{renderer:b.RenderElement("vstrike","X"),bbox:function(t){return[t.padding,0,t.padding,0]}}],["box",{renderer:function(t,e){t.adaptor.setStyle(e,"border",t.em(t.thickness)+" solid")},bbox:b.fullBBox,border:b.fullBorder,remove:"left right top bottom"}],["roundedbox",{renderer:b.RenderElement("rbox"),bbox:b.fullBBox}],["circle",{renderer:b.RenderElement("cbox"),bbox:b.fullBBox}],["phasorangle",{renderer:function(t,e){var r=t.getBBox(),n=(r.w,r.h),i=r.d,o=m(t.getArgMod(1.75*t.padding,n+i),2),a=o[0],s=o[1],c=t.thickness*Math.sin(a)*.9;t.adaptor.setStyle(e,"border-bottom",t.em(t.thickness)+" solid");var l=t.adjustBorder(t.html("mjx-ustrike",{style:{width:t.em(s),transform:"translateX("+t.em(c)+") rotate("+t.fixed(-a)+"rad)"}}));t.adaptor.append(t.chtml,l)},bbox:function(t){var e=t.padding/2,r=t.thickness;return[2*e,e,e+r,3*e+r]},border:function(t){return[0,0,t.thickness,0]},remove:"bottom"}],b.Arrow("up"),b.Arrow("down"),b.Arrow("left"),b.Arrow("right"),b.Arrow("updown"),b.Arrow("leftright"),b.DiagonalArrow("updiagonal"),b.DiagonalArrow("northeast"),b.DiagonalArrow("southeast"),b.DiagonalArrow("northwest"),b.DiagonalArrow("southwest"),b.DiagonalArrow("northeastsouthwest"),b.DiagonalArrow("northwestsoutheast"),["longdiv",{renderer:function(t,e){var r=t.adaptor;r.setStyle(e,"border-top",t.em(t.thickness)+" solid");var n=r.append(t.chtml,t.html("dbox")),i=t.thickness,o=t.padding;i!==b.THICKNESS&&r.setStyle(n,"border-width",t.em(i)),o!==b.PADDING&&(r.setStyle(n,"left",t.em(-1.5*o)),r.setStyle(n,"width",t.em(3*o)),r.setStyle(n,"clip-path","inset(0 0 0 "+t.em(1.5*o)+")"))},bbox:function(t){var e=t.padding,r=t.thickness;return[e+r,e,e,2*e+r/2]}}],["radical",{renderer:function(e,t){e.msqrt.toCHTML(t);var r=e.sqrtTRBL();e.adaptor.setStyle(e.msqrt.chtml,"margin",r.map(function(t){return e.em(-t)}).join(" "))},init:function(t){t.msqrt=t.createMsqrt(t.childNodes[0])},bbox:function(t){return t.sqrtTRBL()},renderChild:!0}]]),f);function f(){return null!==l&&l.apply(this,arguments)||this}e.CHTMLmenclose=h},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),f=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var c=r(36),l=r(10);e.CommonMencloseMixin=function(t){return i(e,n=t),e.prototype.getParameters=function(){var t=this.node.attributes,e=t.get("data-padding");void 0!==e&&(this.padding=this.length2em(e,c.PADDING));var r=t.get("data-thickness");void 0!==r&&(this.thickness=this.length2em(r,c.THICKNESS));var n=t.get("data-arrowhead");if(void 0!==n){var i=f(l.split(n),3),o=i[0],a=i[1],s=i[2];this.arrowhead={x:o?parseFloat(o):c.ARROWX,y:a?parseFloat(a):c.ARROWY,dx:s?parseFloat(s):c.ARROWDX}}},e.prototype.getNotations=function(){var e,t,r=this.constructor.notations;try{for(var n=h(l.split(this.node.attributes.get("notation"))),i=n.next();!i.done;i=n.next()){var o=i.value,a=r.get(o);a&&(this.notations[o]=a).renderChild&&(this.renderChild=a.renderer)}}catch(t){e={error:t}}finally{try{i&&!i.done&&(t=n.return)&&t.call(n)}finally{if(e)throw e.error}}},e.prototype.removeRedundantNotations=function(){var e,t,r,n;try{for(var i=h(Object.keys(this.notations)),o=i.next();!o.done;o=i.next()){var a=o.value;if(this.notations[a]){var s=this.notations[a].remove||"";try{for(var c=(r=void 0,h(s.split(/ /))),l=c.next();!l.done;l=c.next()){var u=l.value;delete this.notations[u]}}catch(t){r={error:t}}finally{try{l&&!l.done&&(n=c.return)&&n.call(c)}finally{if(r)throw r.error}}}}}catch(t){e={error:t}}finally{try{o&&!o.done&&(t=i.return)&&t.call(i)}finally{if(e)throw e.error}}},e.prototype.initializeNotations=function(){var e,t;try{for(var r=h(Object.keys(this.notations)),n=r.next();!n.done;n=r.next()){var i=n.value,o=this.notations[i].init;o&&o(this)}}catch(t){e={error:t}}finally{try{n&&!n.done&&(t=r.return)&&t.call(r)}finally{if(e)throw e.error}}},e.prototype.computeBBox=function(t,e){void 0===e&&(e=!1);var r=f(this.getBBoxExtenders(),4),n=r[0],i=r[1],o=r[2],a=r[3],s=this.childNodes[0].getBBox();t.combine(s,a,0),t.h+=n,t.d+=o,t.w+=i,this.setChildPWidths(e)},e.prototype.getBBoxExtenders=function(){var e,t,r=[0,0,0,0];try{for(var n=h(Object.keys(this.notations)),i=n.next();!i.done;i=n.next()){var o=i.value;this.maximizeEntries(r,this.notations[o].bbox(this))}}catch(t){e={error:t}}finally{try{i&&!i.done&&(t=n.return)&&t.call(n)}finally{if(e)throw e.error}}return r},e.prototype.getPadding=function(){var e,t,r=[0,0,0,0],n=[0,0,0,0];try{for(var i=h(Object.keys(this.notations)),o=i.next();!o.done;o=i.next()){var a=o.value;this.maximizeEntries(r,this.notations[a].bbox(this));var s=this.notations[a].border;s&&this.maximizeEntries(n,s(this))}}catch(t){e={error:t}}finally{try{o&&!o.done&&(t=i.return)&&t.call(i)}finally{if(e)throw e.error}}return[0,1,2,3].map(function(t){return r[t]-n[t]})},e.prototype.maximizeEntries=function(t,e){for(var r=0;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(2),s=r(73),c=r(73),u=r(52),h=(o=s.CommonMrowMixin(a.CHTMLWrapper),i(f,o),f.prototype.toCHTML=function(t){var e,r,n=this.node.isInferred?this.chtml=t:this.standardCHTMLnode(t),i=!1;try{for(var o=l(this.childNodes),a=o.next();!a.done;a=o.next()){var s=a.value;s.toCHTML(n),s.bbox.w<0&&(i=!0)}}catch(t){e={error:t}}finally{try{a&&!a.done&&(r=o.return)&&r.call(o)}finally{if(e)throw e.error}}if(i){var c=this.getBBox().w;c&&(this.adaptor.setStyle(n,"width",this.em(Math.max(0,c))),c<0&&this.adaptor.setStyle(n,"marginRight",this.em(c)))}},f.kind=u.MmlMrow.prototype.kind,f);function f(){return null!==o&&o.apply(this,arguments)||this}e.CHTMLmrow=h;var p,d=(p=c.CommonInferredMrowMixin(h),i(m,p),m.kind=u.MmlInferredMrow.prototype.kind,m);function m(){return null!==p&&p.apply(this,arguments)||this}e.CHTMLinferredMrow=d},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(2),s=r(151),c=r(57),l=(o=s.CommonMfencedMixin(a.CHTMLWrapper),i(u,o),u.prototype.toCHTML=function(t){var e=this.standardCHTMLnode(t);this.mrow.toCHTML(e)},u.kind=c.MmlMfenced.prototype.kind,u);function u(){return null!==o&&o.apply(this,arguments)||this}e.CHTMLmfenced=l},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),o=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0}),e.CommonMfencedMixin=function(t){return i(e,n=t),e.prototype.createMrow=function(){var t=this.node.factory.create("inferredMrow");t.inheritAttributesFrom(this.node),this.mrow=this.wrap(t),this.mrow.parent=this},e.prototype.addMrowChildren=function(){var e,t,r=this.node,n=this.mrow;this.addMo(r.open),this.childNodes.length&&n.childNodes.push(this.childNodes[0]);var i=0;try{for(var o=c(this.childNodes.slice(1)),a=o.next();!a.done;a=o.next()){var s=a.value;this.addMo(r.separators[i++]),n.childNodes.push(s)}}catch(t){e={error:t}}finally{try{a&&!a.done&&(t=o.return)&&t.call(o)}finally{if(e)throw e.error}}this.addMo(r.close),n.stretchChildren()},e.prototype.addMo=function(t){if(t){var e=this.wrap(t);this.mrow.childNodes.push(e),e.parent=this.mrow}},e.prototype.computeBBox=function(t,e){void 0===e&&(e=!1),t.updateFrom(this.mrow.getBBox()),this.setChildPWidths(e)},e;function e(){for(var t=[],e=0;e *":{"font-size":"2000%"},"mjx-dbox":{display:"block","font-size":"5%"},"mjx-num":{display:"block","text-align":"center"},"mjx-den":{display:"block","text-align":"center"},"mjx-mfrac[bevelled] > mjx-num":{display:"inline-block"},"mjx-mfrac[bevelled] > mjx-den":{display:"inline-block"},'mjx-den[align="right"], mjx-num[align="right"]':{"text-align":"right"},'mjx-den[align="left"], mjx-num[align="left"]':{"text-align":"left"},"mjx-nstrut":{display:"inline-block",height:".054em",width:0,"vertical-align":"-.054em"},'mjx-nstrut[type="d"]':{height:".217em","vertical-align":"-.217em"},"mjx-dstrut":{display:"inline-block",height:".505em",width:0},'mjx-dstrut[type="d"]':{height:".726em"},"mjx-line":{display:"block","box-sizing":"border-box","min-height":"1px",height:".06em","border-top":".06em solid",margin:".06em -.1em",overflow:"hidden"},'mjx-line[type="d"]':{margin:".18em -.1em"}},u);function u(){return null!==o&&o.apply(this,arguments)||this}e.CHTMLmfrac=l},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),l=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0this.surdH?(t.h+t.d-(this.surdH-e))/2:e+r/4]},e.prototype.getRootDimens=function(t){return[0,0,0,0]},e;function e(){for(var t=[],e=0;e=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var a,s=r(2),c=r(158),l=(a=c.CommonScriptbaseMixin(s.CHTMLWrapper),i(h,a),h.prototype.toCHTML=function(t){this.chtml=this.standardCHTMLnode(t);var e=o(this.getOffset(this.baseChild.getBBox(),this.script.getBBox()),2),r=e[0],n=e[1],i={"vertical-align":this.em(n)};r&&(i["margin-left"]=this.em(r)),this.baseChild.toCHTML(this.chtml),this.script.toCHTML(this.adaptor.append(this.chtml,this.html("mjx-script",{style:i})))},h.prototype.setDeltaW=function(t,e){for(var r=0;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var a=1.5;e.CommonScriptbaseMixin=function(t){var e,i;return o(r,i=t),Object.defineProperty(r.prototype,"baseChild",{get:function(){return this.childNodes[this.node.base]},enumerable:!0,configurable:!0}),Object.defineProperty(r.prototype,"script",{get:function(){return this.childNodes[1]},enumerable:!0,configurable:!0}),r.prototype.computeBBox=function(t,e){void 0===e&&(e=!1);var r=this.baseChild.getBBox(),n=this.script.getBBox(),i=s(this.getOffset(r,n),2),o=i[0],a=i[1];t.append(r),t.combine(n,t.w+o,a),t.w+=this.font.params.scriptspace,t.clean(),this.setChildPWidths(e)},r.prototype.coreIC=function(){var t=this.baseCore.getBBox();return t.ic?1.2*t.ic+.05:0},r.prototype.isCharBase=function(){var t=this.baseChild;return(t.node.isKind("mstyle")||t.node.isKind("mrow"))&&1===t.childNodes.length&&(t=t.childNodes[0]),(t.node.isKind("mo")||t.node.isKind("mi")||t.node.isKind("mn"))&&1===t.bbox.rscale&&1===t.getText().length&&!t.node.attributes.get("largeop")},r.prototype.getOffset=function(t,e){return[0,0]},r.prototype.getV=function(t,e){var r=this.font.params,n=this.length2em(this.node.attributes.get("subscriptshift"),r.sub1);return Math.max(this.isCharBase()?0:t.d+r.sub_drop*e.rscale,n,e.h*e.rscale-.8*r.x_height)},r.prototype.getU=function(t,e){var r=this.font.params,n=this.node.attributes.getList("displaystyle","texprimestyle","superscriptshift"),i=n.displaystyle?r.sup1:n.texprimestyle?r.sup3:r.sup2,o=this.length2em(n.superscriptshift,i);return Math.max(this.isCharBase()?0:t.h-r.sup_drop*e.rscale,o,e.d*e.rscale+.25*r.x_height)},r.prototype.hasMovableLimits=function(){return!this.node.attributes.get("displaystyle")&&(this.node.getProperty("movablelimits")||this.node.attributes.get("movablelimits")||this.baseChild.coreMO().node.attributes.get("movablelimits"))},r.prototype.getOverKU=function(t,e){var r=this.node.attributes.get("accent"),n=this.font.params,i=e.d*e.rscale,o=(r?n.rule_thickness:Math.max(n.big_op_spacing1,n.big_op_spacing3-Math.max(0,i)))-(this.baseChild.node.isKind("munderover")?.1:0);return[o,t.h*t.rscale+o+i]},r.prototype.getUnderKV=function(t,e){var r=this.node.attributes.get("accentunder"),n=this.font.params,i=e.h*e.rscale,o=(r?n.rule_thickness:Math.max(n.big_op_spacing2,n.big_op_spacing4-i))-(this.baseChild.node.isKind("munderover")?.1:0);return[o,-(t.d*t.rscale+o+i)]},r.prototype.getDeltaW=function(t,e){var r,n,i,o;void 0===e&&(e=[0,0,0]);var a=this.node.attributes.get("align"),s=t.map(function(t){return t.w*t.rscale}),c=Math.max.apply(Math,y(s)),l=[],u=0;try{for(var h=x(s.keys()),f=h.next();!f.done;f=h.next())l[m=f.value]=("center"===a?(c-s[m])/2:"right"===a?c-s[m]:0)+e[m],l[m] mjx-row":{"text-align":"left"},"mjx-under":{"padding-bottom":".1em"}},f);function f(){return null!==c&&c.apply(this,arguments)||this}e.CHTMLmunder=h;var d,m=(d=s.CommonMoverMixin(o.CHTMLmsup),i(y,d),y.prototype.toCHTML=function(t){if(this.hasMovableLimits())return d.prototype.toCHTML.call(this,t),void this.adaptor.setAttribute(this.chtml,"limits","false");this.chtml=this.standardCHTMLnode(t);var e=this.adaptor.append(this.chtml,this.html("mjx-over")),r=this.adaptor.append(this.chtml,this.html("mjx-base"));this.script.toCHTML(e),this.baseChild.toCHTML(r);var n=this.script.getBBox(),i=this.baseChild.getBBox(),o=p(this.getOverKU(i,n),2),a=o[0],s=(o[1],this.getDelta());this.adaptor.setStyle(e,"paddingBottom",this.em(a)),this.setDeltaW([r,e],this.getDeltaW([i,n],[0,s])),this.adjustOverDepth(e,n)},y.kind=u.MmlMover.prototype.kind,y.useIC=!0,y.styles={'mjx-mover:not([limits="false"])':{"padding-top":".1em"},'mjx-mover:not([limits="false"]) > *':{display:"block","text-align":"left"}},y);function y(){return null!==d&&d.apply(this,arguments)||this}e.CHTMLmover=m;var v,b=(v=l.CommonMunderoverMixin(o.CHTMLmsubsup),i(g,v),g.prototype.toCHTML=function(t){if(this.hasMovableLimits())return v.prototype.toCHTML.call(this,t),void this.adaptor.setAttribute(this.chtml,"limits","false");this.chtml=this.standardCHTMLnode(t);var e=this.adaptor.append(this.chtml,this.html("mjx-over")),r=this.adaptor.append(this.adaptor.append(this.chtml,this.html("mjx-box")),this.html("mjx-munder")),n=this.adaptor.append(this.adaptor.append(r,this.html("mjx-row")),this.html("mjx-base")),i=this.adaptor.append(this.adaptor.append(r,this.html("mjx-row")),this.html("mjx-under"));this.overChild.toCHTML(e),this.baseChild.toCHTML(n),this.underChild.toCHTML(i);var o=this.overChild.getBBox(),a=this.baseChild.getBBox(),s=this.underChild.getBBox(),c=p(this.getOverKU(a,o),2),l=c[0],u=(c[1],p(this.getUnderKV(a,s),2)),h=u[0],f=(u[1],this.getDelta());this.adaptor.setStyle(e,"paddingBottom",this.em(l)),this.adaptor.setStyle(i,"paddingTop",this.em(h)),this.setDeltaW([n,i,e],this.getDeltaW([a,s,o],[0,-f,f])),this.adjustOverDepth(e,o),this.adjustUnderDepth(i,s)},g.kind=u.MmlMunderover.prototype.kind,g.useIC=!0,g.styles={'mjx-munderover:not([limits="false"])':{"padding-top":".1em"},'mjx-munderover:not([limits="false"]) > *':{display:"block"}},g);function g(){return null!==v&&v.apply(this,arguments)||this}e.CHTMLmunderover=b},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),c=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0 mjx-row > mjx-cell":{"text-align":"right"}},h);function h(){return null!==o&&o.apply(this,arguments)||this}e.CHTMLmmultiscripts=u},function(t,s,e){"use strict";var n,r=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),d=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(s,"__esModule",{value:!0});var i=e(16);s.NextScript={base:"subList",subList:"supList",supList:"subList",psubList:"psupList",psupList:"psubList"},s.ScriptNames=["sup","sup","psup","psub"],s.CommonMmultiscriptsMixin=function(t){return r(e,c=t),e.prototype.combinePrePost=function(t,e){var r=new i.BBox(t);return r.combine(e,0,0),r},e.prototype.computeBBox=function(t,e){void 0===e&&(e=!1);var r=this.font.params.scriptspace,n=this.getScriptData(),i=this.combinePrePost(n.sub,n.psub),o=this.combinePrePost(n.sup,n.psup),a=d(this.getUVQ(n.base,i,o),2),s=a[0],c=a[1];if(t.empty(),n.numPrescripts&&(t.combine(n.psup,r,s),t.combine(n.psub,r,c)),t.append(n.base),n.numScripts){var l=t.w;t.combine(n.sup,l,s),t.combine(n.sub,l,c),t.w+=r}t.clean(),this.setChildPWidths(e)},e.prototype.getScriptData=function(){if(this.scriptData)return this.scriptData;var t=this.scriptData={base:null,sub:i.BBox.empty(),sup:i.BBox.empty(),psub:i.BBox.empty(),psup:i.BBox.empty(),numPrescripts:0,numScripts:0},e=this.getScriptBBoxLists();return this.combineBBoxLists(t.sub,t.sup,e.subList,e.supList),this.combineBBoxLists(t.psub,t.psup,e.psubList,e.psupList),this.scriptData.base=e.base[0],this.scriptData.numPrescripts=e.psubList.length,this.scriptData.numScripts=e.subList.length,this.scriptData},e.prototype.getScriptBBoxLists=function(){var e,t,r={base:[],subList:[],supList:[],psubList:[],psupList:[]},n="base";try{for(var i=l(this.childNodes),o=i.next();!o.done;o=i.next()){var a=o.value;n=a.node.isKind("mprescripts")?"psubList":(r[n].push(a.getBBox()),s.NextScript[n])}}catch(t){e={error:t}}finally{try{o&&!o.done&&(t=i.return)&&t.call(i)}finally{if(e)throw e.error}}return this.firstPrescript=r.subList.length+r.supList.length+2,this.padLists(r.subList,r.supList),this.padLists(r.psubList,r.psupList),r},e.prototype.padLists=function(t,e){t.length>e.length&&e.push(i.BBox.empty())},e.prototype.combineBBoxLists=function(t,e,r,n){for(var i=0;it.h&&(t.h=s),c>t.d&&(t.d=c),h>e.h&&(e.h=h),f>e.d&&(e.d=f)}},e.prototype.getScaledWHD=function(t){var e=t.w,r=t.h,n=t.d,i=t.rscale;return[e*i,r*i,n*i]},e.prototype.getUVQ=function(t,e,r){var n;if(!this.UVQ){var i=d([0,0,0],3),o=i[0],a=i[1],s=i[2];0===e.h&&0===e.d?o=this.getU(t,r):0===r.h&&0===r.d?o=-this.getV(t,e):(o=(n=d(c.prototype.getUVQ.call(this,t,e,r),3))[0],a=n[1],s=n[2]),this.UVQ=[o,a,s]}return this.UVQ},e;function e(){var t=null!==c&&c.apply(this,arguments)||this;return t.scriptData=null,t.firstPrescript=0,t}var c}},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),y=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},u=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0 mjx-itable":{"vertical-align":"middle","text-align":"left","box-sizing":"border-box"},"mjx-labels > mjx-itable":{position:"absolute",top:0},'mjx-mtable[justify="left"]':{"text-align":"left"},'mjx-mtable[justify="right"]':{"text-align":"right"},'mjx-mtable[justify="left"][side="left"]':{"padding-right":"0 ! important"},'mjx-mtable[justify="left"][side="right"]':{"padding-left":"0 ! important"},'mjx-mtable[justify="right"][side="left"]':{"padding-right":"0 ! important"},'mjx-mtable[justify="right"][side="right"]':{"padding-left":"0 ! important"},"mjx-mtable[align]":{"vertical-align":"baseline"},'mjx-mtable[align="top"] > mjx-table':{"vertical-align":"top"},'mjx-mtable[align="bottom"] > mjx-table':{"vertical-align":"bottom"},'mjx-mtable[align="center"] > mjx-table':{"vertical-align":"middle"},'mjx-mtable[align="baseline"] > mjx-table':{"vertical-align":"middle"}},f);function f(t,e,r){void 0===r&&(r=null);var n=o.call(this,t,e,r)||this;return n.itable=n.html("mjx-itable"),n.labels=n.html("mjx-itable"),n}e.CHTMLmtable=l},function(t,e,r){"use strict";var n,o=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),y=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var s=r(16),v=r(10),b=r(104);e.CommonMtableMixin=function(t){return o(e,i=t),Object.defineProperty(e.prototype,"tableRows",{get:function(){return this.childNodes},enumerable:!0,configurable:!0}),e.prototype.findContainer=function(){for(var t=this,e=t.parent;e&&(e.node.notParent||e.node.isKind("mrow"));)e=(t=e).parent;this.container=e,this.containerI=t.node.childPosition()},e.prototype.getPercentageWidth=function(){if(this.hasLabels)this.bbox.pwidth=s.BBox.fullWidth;else{var t=this.node.attributes.get("width");v.isPercent(t)&&(this.bbox.pwidth=t)}},e.prototype.stretchRows=function(){for(var t=this.node.attributes.get("equalrows"),e=t?this.getEqualRowHeight():0,r=t?this.getTableData():{H:[0],D:[0]},n=r.H,i=r.D,o=this.tableRows,a=0;an[r]&&(n[r]=s),c>i[r]&&(i[r]=c),o&&l>o[e]&&(o[e]=l)},e.prototype.recordPWidthCell=function(t,e){t.childNodes[0]&&t.childNodes[0].getBBox().pwidth&&this.pwidthCells.push([t,e])},e.prototype.computeBBox=function(t,e){void 0===e&&(e=!1);var r,n,i=this.getTableData(),o=i.H,a=i.D;if(this.node.attributes.get("equalrows")){var s=this.getEqualRowHeight();r=b.sum([].concat(this.rLines,this.rSpace))+s*this.numRows}else r=b.sum(o.concat(a,this.rLines,this.rSpace));r+=2*(this.fLine+this.fSpace[1]);var c=this.getComputedWidths();n=b.sum(c.concat(this.cLines,this.cSpace))+2*(this.fLine+this.fSpace[0]);var l=this.node.attributes.get("width");"auto"!==l&&(n=Math.max(this.length2em(l,0)+2*this.fLine,n));var u=y(this.getBBoxHD(r),2),h=u[0],f=u[1];t.h=h,t.d=f,t.w=n;var p=y(this.getBBoxLR(),2),d=p[0],m=p[1];t.L=d,t.R=m,v.isPercent(l)||this.setColumnPWidths()},e.prototype.setChildPWidths=function(t,e,r){var n=this.node.attributes.get("width");if(v.isPercent(n)){this.hasLabels||(this.bbox.pwidth="",this.container.bbox.pwidth="");var i=this.bbox,o=i.w,a=i.L,s=i.R,c=Math.max(o,this.length2em(n,Math.max(e,a+o+s))),l=this.node.attributes.get("equalcolumns")?Array(this.numCols).fill(this.percent(1/Math.max(1,this.numCols))):this.getColumnAttributes("columnwidth",0);this.cWidths=this.getColumnWidthsFixed(l,c);var u=this.getComputedWidths();return this.pWidth=b.sum(u.concat(this.cLines,this.cSpace))+2*(this.fLine+this.fSpace[0]),this.isTop&&(this.bbox.w=this.pWidth),this.setColumnPWidths(),this.pWidth!==o&&this.parent.invalidateBBox(),this.pWidth!==o}},e.prototype.setColumnPWidths=function(){var e,t,r=this.cWidths;try{for(var n=x(this.pwidthCells),i=n.next();!i.done;i=n.next()){var o=y(i.value,2),a=o[0],s=o[1];a.setChildPWidths(!1,r[s])&&(a.invalidateBBox(),a.getBBox())}}catch(t){e={error:t}}finally{try{i&&!i.done&&(t=n.return)&&t.call(n)}finally{if(e)throw e.error}}},e.prototype.getBBoxHD=function(t){var e=y(this.getAlignmentRow(),2),r=e[0],n=e[1];if(null===n){var i=this.font.params.axis_height,o=t/2;return{top:[0,t],center:[o,o],bottom:[t,0],baseline:[o,o],axis:[o+i,o-i]}[r]||[o,o]}var a=this.getVerticalPosition(n,r);return[a,t-a]},e.prototype.getBBoxLR=function(){if(this.hasLabels){var t=this.node.attributes.get("side"),e=y(this.getPadAlignShift(t),3),r=e[0],n=e[1];return e[2],"center"===n?[r,r]:"left"===t?[r,0]:[0,r]}return[0,0]},e.prototype.getPadAlignShift=function(t){var e=this.getTableData().L+this.length2em(this.node.attributes.get("minlabelspacing")),r=y(null==this.styles?["",""]:[this.styles.get("padding-left"),this.styles.get("padding-right")],2),n=r[0],i=r[1];(n||i)&&(e=Math.max(e,this.length2em(n||"0"),this.length2em(i||"0")));var o=y(this.getAlignShift(),2),a=o[0],s=o[1];return a===t&&(s="left"===t?Math.max(e,s)-e:Math.min(-e,s)+e),[e,a,s]},e.prototype.getAlignShift=function(){return this.isTop?i.prototype.getAlignShift.call(this):[this.container.getChildAlign(this.containerI),0]},e.prototype.getWidth=function(){return this.pWidth||this.getBBox().w},e.prototype.getEqualRowHeight=function(){var t=this.getTableData(),e=t.H,r=t.D,n=Array.from(e.keys()).map(function(t){return e[t]+r[t]});return Math.max.apply(Math,n)},e.prototype.getComputedWidths=function(){var e=this,r=this.getTableData().W,t=Array.from(r.keys()).map(function(t){return"number"==typeof e.cWidths[t]?e.cWidths[t]:r[t]});return this.node.attributes.get("equalcolumns")&&(t=Array(t.length).fill(b.max(t))),t},e.prototype.getColumnWidths=function(){var t=this.node.attributes.get("width");if(this.node.attributes.get("equalcolumns"))return this.getEqualColumns(t);var e=this.getColumnAttributes("columnwidth",0);return"auto"===t?this.getColumnWidthsAuto(e):v.isPercent(t)?this.getColumnWidthsPercent(e,t):this.getColumnWidthsFixed(e,this.length2em(t))},e.prototype.getEqualColumns=function(t){var e,r=Math.max(1,this.numCols);if("auto"===t){var n=this.getTableData().W;e=b.max(n)}else if(v.isPercent(t))e=this.percent(1/r);else{var i=b.sum([].concat(this.cLines,this.cSpace))+2*this.fSpace[0];e=Math.max(0,this.length2em(t)-i)/r}return Array(this.numCols).fill(e)},e.prototype.getColumnWidthsAuto=function(t){var e=this;return t.map(function(t){return"auto"===t||"fit"===t?null:v.isPercent(t)?t:e.length2em(t)})},e.prototype.getColumnWidthsPercent=function(r,t){var n=this,i=0<=r.indexOf("fit"),o=(i?this.getTableData():{W:null}).W;return Array.from(r.keys()).map(function(t){var e=r[t];return"fit"===e?null:"auto"===e?i?o[t]:null:v.isPercent(e)?e:n.length2em(e)})},e.prototype.getColumnWidthsFixed=function(r,n){var i=this,t=Array.from(r.keys()),o=t.filter(function(t){return"fit"===r[t]}),e=t.filter(function(t){return"auto"===r[t]}),a=o.length||e.length,s=(a?this.getTableData():{W:null}).W,c=n-b.sum([].concat(this.cLines,this.cSpace))-2*this.fSpace[0],l=c;t.forEach(function(t){var e=r[t];l-="fit"===e||"auto"===e?s[t]:i.length2em(e,n)});var u=a&&0this.numRows?null:n-1]},e.prototype.getColumnAttributes=function(t,e){void 0===e&&(e=1);var r=this.numCols-e,n=this.getAttributeArray(t);if(0!==n.length){for(;n.lengthr&&n.splice(r),n}},e.prototype.getRowAttributes=function(t,e){void 0===e&&(e=1);var r=this.numRows-e,n=this.getAttributeArray(t);if(0!==n.length){for(;n.lengthr&&n.splice(r),n}},e.prototype.getAttributeArray=function(t){var e=this.node.attributes.get(t);return e?v.split(e):[this.node.attributes.getDefault(t)]},e.prototype.addEm=function(t,e){var r=this;if(void 0===e&&(e=1),t)return t.map(function(t){return r.em(t/e)})},e.prototype.convertLengths=function(t){var e=this;if(t)return t.map(function(t){return e.length2em(t)})},e;function e(){for(var t=[],e=0;e mjx-mtd':{"vertical-align":"top"},'mjx-mtr[rowalign="center"] > mjx-mtd':{"vertical-align":"middle"},'mjx-mtr[rowalign="bottom"] > mjx-mtd':{"vertical-align":"bottom"},'mjx-mtr[rowalign="baseline"] > mjx-mtd':{"vertical-align":"baseline"},'mjx-mtr[rowalign="axis"] > mjx-mtd':{"vertical-align":".25em"}},h);function h(){return null!==o&&o.apply(this,arguments)||this}e.CHTMLmtr=u;var f,p=(f=c.CommonMlabeledtrMixin(u),i(d,f),d.prototype.toCHTML=function(t){f.prototype.toCHTML.call(this,t);var e=this.adaptor.firstChild(this.chtml);if(e){this.adaptor.remove(e);var r=this.node.attributes.get("rowalign"),n="baseline"!==r&&"axis"!==r?{rowalign:r}:{},i=this.html("mjx-mtr",n,[e]);this.adaptor.append(this.parent.labels,i)}},d.kind=l.MmlMlabeledtr.prototype.kind,d.styles={"mjx-mlabeledtr":{display:"table-row"},'mjx-mlabeledtr[rowalign="top"] > mjx-mtd':{"vertical-align":"top"},'mjx-mlabeledtr[rowalign="center"] > mjx-mtd':{"vertical-align":"middle"},'mjx-mlabeledtr[rowalign="bottom"] > mjx-mtd':{"vertical-align":"bottom"},'mjx-mlabeledtr[rowalign="baseline"] > mjx-mtd':{"vertical-align":"baseline"},'mjx-mlabeledtr[rowalign="axis"] > mjx-mtd':{"vertical-align":".25em"}},d);function d(){return null!==f&&f.apply(this,arguments)||this}e.CHTMLmlabeledtr=p},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(2),s=r(166),c=r(64),l=(o=s.CommonMtdMixin(a.CHTMLWrapper),i(u,o),u.prototype.toCHTML=function(t){o.prototype.toCHTML.call(this,t);var e=this.node.attributes.get("rowalign"),r=this.node.attributes.get("columnalign");e!==this.parent.node.attributes.get("rowalign")&&this.adaptor.setAttribute(this.chtml,"rowalign",e),"center"===r||"mlabeledtr"===this.parent.kind&&this===this.parent.childNodes[0]&&r===this.parent.parent.node.attributes.get("side")||this.adaptor.setStyle(this.chtml,"textAlign",r),this.adaptor.append(this.chtml,this.html("mjx-tstrut"))},u.kind=c.MmlMtd.prototype.kind,u.styles={"mjx-mtd":{display:"table-cell","text-align":"center",padding:".215em .4em"},"mjx-mtd:first-child":{"padding-left":0},"mjx-mtd:last-child":{"padding-right":0},"mjx-mtable > * > mjx-itable > *:first-child > mjx-mtd":{"padding-top":0},"mjx-mtable > * > mjx-itable > *:last-child > mjx-mtd":{"padding-bottom":0},"mjx-tstrut":{display:"inline-block",height:"1em","vertical-align":"-.25em"},'mjx-labels[align="left"] > mjx-mtr > mjx-mtd':{"text-align":"left"},'mjx-labels[align="right"] > mjx-mtr > mjx-mtd':{"text-align":"right"},'mjx-mtr mjx-mtd[rowalign="top"], mjx-mlabeledtr mjx-mtd[rowalign="top"]':{"vertical-align":"top"},'mjx-mtr mjx-mtd[rowalign="center"], mjx-mlabeledtr mjx-mtd[rowalign="center"]':{"vertical-align":"middle"},'mjx-mtr mjx-mtd[rowalign="bottom"], mjx-mlabeledtr mjx-mtd[rowalign="bottom"]':{"vertical-align":"bottom"},'mjx-mtr mjx-mtd[rowalign="baseline"], mjx-mlabeledtr mjx-mtd[rowalign="baseline"]':{"vertical-align":"baseline"},'mjx-mtr mjx-mtd[rowalign="axis"], mjx-mlabeledtr mjx-mtd[rowalign="axis"]':{"vertical-align":".25em"}},u);function u(){return null!==o&&o.apply(this,arguments)||this}e.CHTMLmtd=l},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0}),e.CommonMtdMixin=function(t){return i(e,r=t),Object.defineProperty(e.prototype,"fixesPWidth",{get:function(){return!1},enumerable:!0,configurable:!0}),e.prototype.invalidateBBox=function(){this.bboxComputed=!1},e.prototype.getWrapWidth=function(t){var e=this.parent.parent,r=this.parent,n=this.node.childPosition()-(r.labeled?1:0);return"number"==typeof e.cWidths[n]?e.cWidths[n]:e.getTableData().W[n]},e.prototype.getChildAlign=function(t){return this.node.attributes.get("columnalign")},e;function e(){return null!==r&&r.apply(this,arguments)||this}var r}},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(2),s=r(76),c=r(76),l=r(59),u=(o=s.CommonMactionMixin(a.CHTMLWrapper),i(h,o),h.prototype.toCHTML=function(t){var e=this.standardCHTMLnode(t);this.selected.toCHTML(e),this.action(this,this.data)},h.prototype.setEventHandler=function(t,e){this.chtml.addEventListener(t,e)},h.kind=l.MmlMaction.prototype.kind,h.styles={"mjx-maction":{position:"relative"},"mjx-maction > mjx-tool":{display:"none",position:"absolute",bottom:0,right:0,width:0,height:0,"z-index":500},"mjx-tool > mjx-tip":{display:"inline-block",padding:".2em",border:"1px solid #888","font-size":"70%","background-color":"#F8F8F8",color:"black","box-shadow":"2px 2px 5px #AAAAAA"},"mjx-maction[toggle]":{cursor:"pointer"},"mjx-status":{display:"block",position:"fixed",left:"1em",bottom:"1em","min-width":"25%",padding:".2em .4em",border:"1px solid #888","font-size":"90%","background-color":"#F8F8F8",color:"black"}},h.actions=new Map([["toggle",[function(t,e){t.adaptor.setAttribute(t.chtml,"toggle",t.node.attributes.get("selection"));var r=t.factory.jax.math,n=t.factory.jax.document,i=t.node;t.setEventHandler("click",function(t){r.start.node||(r.start.node=r.end.node=r.typesetRoot,r.start.n=r.end.n=0),i.nextToggleSelection(),r.rerender(n),t.stopPropagation()})},{}]],["tooltip",[function(r,n){var t=r.childNodes[1];if(t)if(t.node.isKind("mtext")){var e=t.node.getText();r.adaptor.setAttribute(r.chtml,"title",e)}else{var i=r.adaptor,o=i.append(r.chtml,r.html("mjx-tool",{style:{bottom:r.em(-r.dy),right:r.em(-r.dx)}},[r.html("mjx-tip")]));t.toCHTML(i.firstChild(o)),r.setEventHandler("mouseover",function(t){n.stopTimers(r,n);var e=setTimeout(function(){return i.setStyle(o,"display","block")},n.postDelay);n.hoverTimer.set(r,e),t.stopPropagation()}),r.setEventHandler("mouseout",function(t){n.stopTimers(r,n);var e=setTimeout(function(){return i.setStyle(o,"display","")},n.clearDelay);n.clearTimer.set(r,e),t.stopPropagation()})}},c.TooltipData]],["statusline",[function(r,n){var t=r.childNodes[1];if(t&&t.node.isKind("mtext")){var i=r.adaptor,o=t.node.getText();i.setAttribute(r.chtml,"statusline",o),r.setEventHandler("mouseover",function(t){if(null===n.status){var e=i.body(i.document);n.status=i.append(e,r.html("mjx-status",{},[r.text(o)]))}t.stopPropagation()}),r.setEventHandler("mouseout",function(t){n.status&&(i.remove(n.status),n.status=null),t.stopPropagation()})}},{status:null}]]]),h);function h(){return null!==o&&o.apply(this,arguments)||this}e.CHTMLmaction=u},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(2),s=r(169),c=r(65),l=(o=s.CommonMglyphMixin(a.CHTMLWrapper),i(u,o),u.prototype.toCHTML=function(t){var e=this.standardCHTMLnode(t),r=this.node.attributes.getList("src","alt"),n=r.src,i=r.alt,o={width:this.em(this.width),height:this.em(this.height)};this.voffset&&(o.verticalAlign=this.em(-this.voffset));var a=this.html("img",{src:n,style:o,alt:i,title:i});this.adaptor.append(e,a)},u.kind=c.MmlMglyph.prototype.kind,u.styles={"mjx-mglyph > img":{display:"inline-block",border:0,padding:0}},u);function u(){return null!==o&&o.apply(this,arguments)||this}e.CHTMLmglyph=l},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),o=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(0),s=r(2),c=r(175),l=(o=c.CommonTextNodeMixin(s.CHTMLWrapper),i(u,o),u.prototype.toCHTML=function(t){var e,r;this.markUsed();var n=this.adaptor,i=this.parent.variant,o=this.node.getText();if("-explicitFont"===i){var a=this.jax.getFontData(this.parent.styles);n.append(t,this.jax.unknownText(o,i,a))}else{var s=this.parent.stretch.c,c=this.parent.remapChars(s?[s]:this.unicodeChars(o));try{for(var l=d(c),u=l.next();!u.done;u=l.next()){var h=u.value,f=this.getVariantChar(i,h)[3],p=f.unknown?this.jax.unknownText(String.fromCharCode(h),i):this.html("mjx-c",{class:this.char(h)});n.append(t,p),f.used=!0}}catch(t){e={error:t}}finally{try{u&&!u.done&&(r=l.return)&&r.call(l)}finally{if(e)throw e.error}}}},u.kind=a.TextNode.prototype.kind,u.autoStyle=!1,u.styles={"mjx-c":{display:"inline-block"},"mjx-utext":{display:"inline-block",padding:".75em 0 .25em 0"},"mjx-measure-text":{position:"absolute","font-family":"MJXZERO","white-space":"nowrap",height:"1px",width:"1px",overflow:"hidden"}},u);function u(){return null!==o&&o.apply(this,arguments)||this}e.CHTMLTextNode=l},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),g=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},M=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0t.h&&(t.h=l),u>t.d&&(t.d=u),t.ic=v.ic||0,t.sk=v.sk||0}}catch(t){r={error:t}}finally{try{d&&!d.done&&(n=p.return)&&n.call(p)}finally{if(r)throw r.error}}1"},63:{c:"?"},64:{c:"@"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},91:{c:"["},93:{c:"]"},94:{c:"^"},95:{c:"_"},96:{c:"`"},97:{c:"a"},98:{c:"b"},99:{c:"c"},100:{c:"d"},101:{c:"e"},102:{c:"f"},103:{c:"g"},104:{c:"h"},105:{c:"i"},106:{c:"j"},107:{c:"k"},108:{c:"l"},109:{c:"m"},110:{c:"n"},111:{c:"o"},112:{c:"p"},113:{c:"q"},114:{c:"r"},115:{c:"s"},116:{c:"t"},117:{c:"u"},118:{c:"v"},119:{c:"w"},120:{c:"x"},121:{c:"y"},122:{c:"z"},123:{c:"{"},124:{c:"|"},125:{c:"}"},126:{c:"~"},183:{c:"\\22C5"},697:{c:"\\2032"},913:{c:"A"},914:{c:"B"},917:{c:"E"},918:{c:"Z"},919:{c:"H"},921:{c:"I"},922:{c:"K"},924:{c:"M"},925:{c:"N"},927:{c:"O"},929:{c:"P"},930:{c:"\\398"},932:{c:"T"},935:{c:"X"},978:{c:"\\3A5"},988:{c:"F"},8194:{c:""},8195:{c:""},8196:{c:""},8197:{c:""},8198:{c:""},8201:{c:""},8202:{c:""},8213:{c:"\\2014"},8214:{c:"\\2225"},8215:{c:"_"},8226:{c:"\\2219"},8243:{c:"\\2032\\2032"},8244:{c:"\\2032\\2032\\2032"},8254:{c:"\\2C9"},8260:{c:"/"},8279:{c:"\\2032\\2032\\2032\\2032"},8407:{c:"\\2192",f:"VB"},8465:{c:"I",f:"FR"},8476:{c:"R",f:"FR"},8602:{c:"\\2190\\338"},8603:{c:"\\2192\\338"},8622:{c:"\\2194\\338"},8653:{c:"\\21D0\\338"},8654:{c:"\\21D4\\338"},8655:{c:"\\21D2\\338"},8708:{c:"\\2203\\338"},8710:{c:"\\394"},8716:{c:"\\220B\\338"},8740:{c:"\\2223\\338"},8742:{c:"\\2225\\338"},8769:{c:"\\223C\\338"},8772:{c:"\\2243\\338"},8775:{c:"\\2245\\338"},8777:{c:"\\2248\\338"},8802:{c:"\\2261\\338"},8813:{c:"\\224D\\338"},8814:{c:"<\\338"},8815:{c:">\\338"},8816:{c:"\\2264\\338"},8817:{c:"\\2265\\338"},8832:{c:"\\227A\\338"},8833:{c:"\\227B\\338"},8836:{c:"\\2282\\338"},8837:{c:"\\2283\\338"},8840:{c:"\\2286\\338"},8841:{c:"\\2287\\338"},8876:{c:"\\22A2\\338"},8877:{c:"\\22A8\\338"},8930:{c:"\\2291\\338"},8931:{c:"\\2292\\338"},9001:{c:"\\27E8"},9002:{c:"\\27E9"},9653:{c:"\\25B3"},9663:{c:"\\25BD"},10072:{c:"\\2223"},10744:{c:"/",f:"BI"},10799:{c:"\\D7"},12296:{c:"\\27E8"},12297:{c:"\\27E9"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.bold={32:[0,0,.25],33:[.705,0,.35],34:[.694,-.329,.603],35:[.694,.193,.958],36:[.75,.056,.575],37:[.75,.056,.958],38:[.705,.011,.894],39:[.694,-.329,.319],40:[.75,.249,.447],41:[.75,.249,.447],42:[.75,-.306,.575],43:[.633,.131,.894],44:[.171,.194,.319],45:[.278,-.166,.383],46:[.171,0,.319],47:[.75,.25,.575],48:[.654,.01,.575],49:[.655,0,.575],50:[.654,0,.575],51:[.655,.011,.575],52:[.656,0,.575],53:[.655,.011,.575],54:[.655,.011,.575],55:[.676,.011,.575],56:[.654,.011,.575],57:[.654,.011,.575],58:[.444,0,.319],59:[.444,.194,.319],60:[.587,.085,.894],61:[.393,-.109,.894],62:[.587,.085,.894],63:[.7,0,.543],64:[.699,.006,.894],65:[.698,0,.869],66:[.686,0,.818],67:[.697,.011,.831],68:[.686,0,.882],69:[.68,0,.756],70:[.68,0,.724],71:[.697,.01,.904],72:[.686,0,.9],73:[.686,0,.436],74:[.686,.011,.594],75:[.686,0,.901],76:[.686,0,.692],77:[.686,0,1.092],78:[.686,0,.9],79:[.696,.01,.864],80:[.686,0,.786],81:[.696,.193,.864],82:[.686,.011,.862],83:[.697,.011,.639],84:[.675,0,.8],85:[.686,.011,.885],86:[.686,.007,.869],87:[.686,.007,1.189],88:[.686,0,.869],89:[.686,0,.869],90:[.686,0,.703],91:[.75,.25,.319],92:[.75,.25,.575],93:[.75,.25,.319],94:[.694,-.52,.575],95:[-.01,.061,.575],96:[.706,-.503,.575],97:[.453,.006,.559],98:[.694,.006,.639],99:[.453,.006,.511],100:[.694,.006,.639],101:[.452,.006,.527],102:[.7,0,.351,{ic:.101}],103:[.455,.201,.575],104:[.694,0,.639],105:[.695,0,.319],106:[.695,.2,.351],107:[.694,0,.607],108:[.694,0,.319],109:[.45,0,.958],110:[.45,0,.639],111:[.452,.005,.575],112:[.45,.194,.639],113:[.45,.194,.607],114:[.45,0,.474],115:[.453,.006,.454],116:[.635,.005,.447],117:[.45,.006,.639],118:[.444,0,.607],119:[.444,0,.831],120:[.444,0,.607],121:[.444,.2,.607],122:[.444,0,.511],123:[.75,.25,.575],124:[.75,.249,.319],125:[.75,.25,.575],126:[.344,-.202,.575],160:[0,0,.25],168:[.695,-.535,.575],172:[.371,-.061,.767],175:[.607,-.54,.575],176:[.702,-.536,.575],177:[.728,.035,.894],180:[.706,-.503,.575],183:[.336,-.166,.319],215:[.53,.028,.894],247:[.597,.096,.894],305:[.452,.008,.394,{sk:.0319}],567:[.451,.201,.439,{sk:.0958}],697:[.563,-.033,.344],710:[.694,-.52,.575],711:[.66,-.515,.575],713:[.607,-.54,.575],714:[.706,-.503,.575],715:[.706,-.503,.575],728:[.694,-.5,.575],729:[.695,-.525,.575],730:[.702,-.536,.575],732:[.694,-.552,.575],768:[.706,-.503,0],769:[.706,-.503,0],770:[.694,-.52,0],771:[.694,-.552,0],772:[.607,-.54,0],774:[.694,-.5,0],775:[.695,-.525,0],776:[.695,-.535,0],778:[.702,-.536,0],779:[.714,-.511,0],780:[.66,-.515,0],824:[.711,.21,0],913:[.698,0,.869],914:[.686,0,.818],915:[.68,0,.692],916:[.698,0,.958],917:[.68,0,.756],918:[.686,0,.703],919:[.686,0,.9],920:[.696,.01,.894],921:[.686,0,.436],922:[.686,0,.901],923:[.698,0,.806],924:[.686,0,1.092],925:[.686,0,.9],926:[.675,0,.767],927:[.696,.01,.864],928:[.68,0,.9],929:[.686,0,.786],930:[.696,.01,.894],931:[.686,0,.831],932:[.675,0,.8],933:[.697,0,.894],934:[.686,0,.831],935:[.686,0,.869],936:[.686,0,.894],937:[.696,0,.831],945:[.452,.008,.761,{sk:.0319}],946:[.701,.194,.66,{sk:.0958}],947:[.451,.211,.59],948:[.725,.008,.522,{sk:.0639}],949:[.461,.017,.529,{sk:.0958}],950:[.711,.202,.508,{sk:.0958}],951:[.452,.211,.6,{sk:.0639}],952:[.702,.008,.562,{sk:.0958}],953:[.452,.008,.412,{sk:.0639}],954:[.452,.008,.668],955:[.694,.013,.671],956:[.452,.211,.708,{sk:.0319}],957:[.452,0,.577,{sk:.0319}],958:[.711,.201,.508,{sk:.128}],959:[.452,.008,.585,{sk:.0639}],960:[.444,.008,.682],961:[.451,.211,.612,{sk:.0958}],962:[.451,.105,.424,{sk:.0958}],963:[.444,.008,.686],964:[.444,.013,.521,{ic:.089,sk:.0319}],965:[.453,.008,.631,{sk:.0319}],966:[.452,.216,.747,{sk:.0958}],967:[.452,.201,.718,{sk:.0639}],968:[.694,.202,.758,{sk:.128}],969:[.453,.008,.718],977:[.701,.008,.692,{sk:.0958}],978:[.697,0,.894],981:[.694,.202,.712,{sk:.0958}],982:[.444,.008,.975],988:[.68,0,.724],1009:[.451,.194,.612,{sk:.0958}],1013:[.444,.007,.483,{sk:.0639}],8194:[0,0,.5],8195:[0,0,.999],8196:[0,0,.333],8197:[0,0,.25],8198:[0,0,.167],8201:[0,0,.167],8202:[0,0,.083],8211:[.3,-.249,.575],8212:[.3,-.249,1.15],8213:[.3,-.249,1.15],8214:[.75,.248,.575],8215:[-.01,.061,.575],8216:[.694,-.329,.319],8217:[.694,-.329,.319],8220:[.694,-.329,.603],8221:[.694,-.329,.603],8224:[.702,.211,.511],8225:[.702,.202,.511],8226:[.474,-.028,.575],8230:[.171,0,1.295],8242:[.563,-.033,.344],8243:[.563,0,.688],8244:[.563,0,1.032],8254:[.607,-.54,.575],8260:[.75,.25,.575],8279:[.563,0,1.376],8407:[.723,-.513,.575],8463:[.694,.008,.668,{sk:-.0319}],8465:[.686,.026,.554],8467:[.702,.019,.474,{sk:.128}],8472:[.461,.21,.74],8476:[.686,.026,.828],8501:[.694,0,.703],8592:[.518,.017,1.15],8593:[.694,.193,.575],8594:[.518,.017,1.15],8595:[.694,.194,.575],8596:[.518,.017,1.15],8597:[.767,.267,.575],8598:[.724,.194,1.15],8599:[.724,.193,1.15],8600:[.694,.224,1.15],8601:[.694,.224,1.15],8602:[.711,.21,1.15],8603:[.711,.21,1.15],8614:[.518,.017,1.15],8617:[.518,.017,1.282],8618:[.518,.017,1.282],8622:[.711,.21,1.15],8636:[.518,-.22,1.15],8637:[.281,.017,1.15],8640:[.518,-.22,1.15],8641:[.281,.017,1.15],8652:[.718,.017,1.15],8653:[.711,.21,1.15],8654:[.711,.21,1.15],8655:[.711,.21,1.15],8656:[.547,.046,1.15],8657:[.694,.193,.703],8658:[.547,.046,1.15],8659:[.694,.194,.703],8660:[.547,.046,1.15],8661:[.767,.267,.703],8704:[.694,.016,.639],8706:[.71,.017,.628,{sk:.0958}],8707:[.694,0,.639],8708:[.711,.21,.639],8709:[.767,.073,.575],8710:[.698,0,.958],8711:[.686,.024,.958],8712:[.587,.086,.767],8713:[.711,.21,.767],8715:[.587,.086,.767],8716:[.711,.21,.767],8722:[.281,-.221,.894],8723:[.537,.227,.894],8725:[.75,.25,.575],8726:[.75,.25,.575],8727:[.472,-.028,.575],8728:[.474,-.028,.575],8729:[.474,-.028,.575],8730:[.82,.18,.958],8733:[.451,.008,.894],8734:[.452,.008,1.15],8736:[.714,0,.722],8739:[.75,.249,.319],8740:[.75,.249,.319],8741:[.75,.248,.575],8742:[.75,.248,.575],8743:[.604,.017,.767],8744:[.604,.016,.767],8745:[.603,.016,.767],8746:[.604,.016,.767],8747:[.711,.211,.569,{ic:.063}],8764:[.391,-.109,.894],8768:[.583,.082,.319],8769:[.711,.21,.894],8771:[.502,0,.894],8772:[.711,.21,.894],8773:[.638,.027,.894],8775:[.711,.21,.894],8776:[.524,-.032,.894],8777:[.711,.21,.894],8781:[.533,.032,.894],8784:[.721,-.109,.894],8800:[.711,.21,.894],8801:[.505,0,.894],8802:[.711,.21,.894],8804:[.697,.199,.894],8805:[.697,.199,.894],8810:[.617,.116,1.15],8811:[.618,.116,1.15],8813:[.711,.21,.894],8814:[.711,.21,.894],8815:[.711,.21,.894],8816:[.711,.21,.894],8817:[.711,.21,.894],8826:[.585,.086,.894],8827:[.586,.086,.894],8832:[.711,.21,.894],8833:[.711,.21,.894],8834:[.587,.085,.894],8835:[.587,.086,.894],8836:[.711,.21,.894],8837:[.711,.21,.894],8838:[.697,.199,.894],8839:[.697,.199,.894],8840:[.711,.21,.894],8841:[.711,.21,.894],8846:[.604,.016,.767],8849:[.697,.199,.894],8850:[.697,.199,.894],8851:[.604,0,.767],8852:[.604,0,.767],8853:[.632,.132,.894],8854:[.632,.132,.894],8855:[.632,.132,.894],8856:[.632,.132,.894],8857:[.632,.132,.894],8866:[.693,0,.703],8867:[.693,0,.703],8868:[.694,0,.894],8869:[.693,0,.894],8872:[.75,.249,.974],8876:[.711,.21,.703],8877:[.75,.249,.974],8900:[.523,.021,.575],8901:[.336,-.166,.319],8902:[.502,0,.575],8904:[.54,.039,1],8930:[.711,.21,.894],8931:[.711,.21,.894],8942:[.951,.029,.319],8943:[.336,-.166,1.295],8945:[.871,-.101,1.323],8968:[.75,.248,.511],8969:[.75,.248,.511],8970:[.749,.248,.511],8971:[.749,.248,.511],8994:[.405,-.108,1.15],8995:[.392,-.126,1.15],9001:[.75,.249,.447],9002:[.75,.249,.447],9651:[.711,0,1.022],9653:[.711,0,1.022],9657:[.54,.039,.575],9661:[.5,.21,1.022],9663:[.5,.21,1.022],9667:[.539,.038,.575],9711:[.711,.211,1.15],9824:[.719,.129,.894],9825:[.711,.024,.894],9826:[.719,.154,.894],9827:[.719,.129,.894],9837:[.75,.017,.447],9838:[.741,.223,.447],9839:[.724,.224,.447],10072:[.75,.249,.319],10216:[.75,.249,.447],10217:[.75,.249,.447],10229:[.518,.017,1.805],10230:[.518,.017,1.833],10231:[.518,.017,2.126],10232:[.547,.046,1.868],10233:[.547,.046,1.87],10234:[.547,.046,2.126],10236:[.518,.017,1.833],10744:[.711,.21,.894],10799:[.53,.028,.894],10815:[.686,0,.9],10927:[.696,.199,.894],10928:[.697,.199,.894],12296:[.75,.249,.447],12297:[.75,.249,.447]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(181);e.doubleStruck=n.AddCSS(i.doubleStruck,{32:{c:" "},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},107:{c:"k"},913:{c:"A",f:"B"},914:{c:"B",f:"B"},917:{c:"E",f:"B"},918:{c:"Z",f:"B"},919:{c:"H",f:"B"},921:{c:"I",f:"B"},922:{c:"K",f:"B"},924:{c:"M",f:"B"},925:{c:"N",f:"B"},927:{c:"O",f:"B"},929:{c:"P",f:"B"},930:{c:"\\398",f:"B"},932:{c:"T",f:"B"},935:{c:"X",f:"B"},978:{c:"\\3A5",f:"B"},988:{c:"F",f:"B"},8450:{c:"C",f:"A"},8461:{c:"H",f:"A"},8469:{c:"N",f:"A"},8473:{c:"P",f:"A"},8474:{c:"Q",f:"A"},8477:{c:"R",f:"A"},8484:{c:"Z",f:"A"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.doubleStruck={32:[0,0,.25],65:[.701,0,.722],66:[.683,0,.667],67:[.702,.019,.722],68:[.683,0,.722],69:[.683,0,.667],70:[.683,0,.611],71:[.702,.019,.778],72:[.683,0,.778],73:[.683,0,.389],74:[.683,.077,.5],75:[.683,0,.778],76:[.683,0,.667],77:[.683,0,.944],78:[.683,.02,.722],79:[.701,.019,.778],80:[.683,0,.611],81:[.701,.181,.778],82:[.683,0,.722],83:[.702,.012,.556],84:[.683,0,.667],85:[.683,.019,.722],86:[.683,.02,.722],87:[.683,.019,1],88:[.683,0,.722],89:[.683,0,.722],90:[.683,0,.667],107:[.683,0,.556],160:[0,0,.25],913:[.698,0,.869],914:[.686,0,.818],917:[.68,0,.756],918:[.686,0,.703],919:[.686,0,.9],921:[.686,0,.436],922:[.686,0,.901],924:[.686,0,1.092],925:[.686,0,.9],927:[.696,.01,.864],929:[.686,0,.786],930:[.696,.01,.894],932:[.675,0,.8],935:[.686,0,.869],978:[.697,0,.894],988:[.68,0,.724],8450:[.702,.019,.722],8461:[.683,0,.778],8469:[.683,.02,.722],8473:[.683,0,.611],8474:[.701,.181,.778],8477:[.683,0,.722],8484:[.683,0,.667]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(183);e.frakturBold=n.AddCSS(i.frakturBold,{32:{c:" "},33:{c:"!"},38:{c:"&"},40:{c:"("},41:{c:")"},42:{c:"*"},43:{c:"+"},44:{c:","},45:{c:"-"},46:{c:"."},47:{c:"/"},48:{c:"0"},49:{c:"1"},50:{c:"2"},51:{c:"3"},52:{c:"4"},53:{c:"5"},54:{c:"6"},55:{c:"7"},56:{c:"8"},57:{c:"9"},58:{c:":"},59:{c:";"},61:{c:"="},63:{c:"?"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},91:{c:"["},93:{c:"]"},94:{c:"^"},97:{c:"a"},98:{c:"b"},99:{c:"c"},100:{c:"d"},101:{c:"e"},102:{c:"f"},103:{c:"g"},104:{c:"h"},105:{c:"i"},106:{c:"j"},107:{c:"k"},108:{c:"l"},109:{c:"m"},110:{c:"n"},111:{c:"o"},112:{c:"p"},113:{c:"q"},114:{c:"r"},115:{c:"s"},116:{c:"t"},117:{c:"u"},118:{c:"v"},119:{c:"w"},120:{c:"x"},121:{c:"y"},122:{c:"z"},913:{c:"A",f:"B"},914:{c:"B",f:"B"},917:{c:"E",f:"B"},918:{c:"Z",f:"B"},919:{c:"H",f:"B"},921:{c:"I",f:"B"},922:{c:"K",f:"B"},924:{c:"M",f:"B"},925:{c:"N",f:"B"},927:{c:"O",f:"B"},929:{c:"P",f:"B"},930:{c:"\\398",f:"B"},932:{c:"T",f:"B"},935:{c:"X",f:"B"},978:{c:"\\3A5",f:"B"},988:{c:"F",f:"B"},8260:{c:"/"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.frakturBold={32:[0,0,.25],33:[.689,.012,.349],34:[.695,-.432,.254],38:[.696,.016,.871],39:[.695,-.436,.25],40:[.737,.186,.459],41:[.735,.187,.459],42:[.692,-.449,.328],43:[.598,.082,.893],44:[.107,.191,.328],45:[.275,-.236,.893],46:[.102,.015,.328],47:[.721,.182,.593],48:[.501,.012,.593],49:[.489,0,.593],50:[.491,0,.593],51:[.487,.193,.593],52:[.495,.196,.593],53:[.481,.19,.593],54:[.704,.012,.593],55:[.479,.197,.593],56:[.714,.005,.593],57:[.487,.195,.593],58:[.457,.012,.255],59:[.458,.19,.255],61:[.343,-.168,.582],63:[.697,.014,.428],65:[.686,.031,.847],66:[.684,.031,1.044],67:[.676,.032,.723],68:[.683,.029,.982],69:[.686,.029,.783],70:[.684,.146,.722],71:[.687,.029,.927],72:[.683,.126,.851],73:[.681,.025,.655],74:[.68,.141,.652],75:[.681,.026,.789],76:[.683,.028,.786],77:[.683,.032,1.239],78:[.679,.03,.983],79:[.726,.03,.976],80:[.688,.223,.977],81:[.726,.083,.976],82:[.688,.028,.978],83:[.685,.031,.978],84:[.686,.03,.79],85:[.688,.039,.851],86:[.685,.029,.982],87:[.683,.03,1.235],88:[.681,.035,.849],89:[.688,.214,.984],90:[.677,.148,.711],91:[.74,.13,.257],93:[.738,.132,.257],94:[.734,-.452,.59],97:[.472,.032,.603],98:[.69,.032,.59],99:[.473,.026,.464],100:[.632,.028,.589],101:[.471,.027,.472],102:[.687,.222,.388],103:[.472,.208,.595],104:[.687,.207,.615],105:[.686,.025,.331],106:[.682,.203,.332],107:[.682,.025,.464],108:[.681,.024,.337],109:[.476,.031,.921],110:[.473,.028,.654],111:[.482,.034,.609],112:[.557,.207,.604],113:[.485,.211,.596],114:[.472,.026,.46],115:[.479,.034,.523],116:[.648,.027,.393],117:[.472,.032,.589],118:[.546,.027,.604],119:[.549,.032,.918],120:[.471,.188,.459],121:[.557,.221,.589],122:[.471,.214,.461],160:[0,0,.25],913:[.698,0,.869],914:[.686,0,.818],917:[.68,0,.756],918:[.686,0,.703],919:[.686,0,.9],921:[.686,0,.436],922:[.686,0,.901],924:[.686,0,1.092],925:[.686,0,.9],927:[.696,.01,.864],929:[.686,0,.786],930:[.696,.01,.894],932:[.675,0,.8],935:[.686,0,.869],978:[.697,0,.894],988:[.68,0,.724],8216:[.708,-.411,.254],8217:[.692,-.394,.254],8260:[.721,.182,.593],58113:[.63,.027,.587],58114:[.693,.212,.394],58115:[.681,.219,.387],58116:[.473,.212,.593],58117:[.684,.027,.393],58120:[.679,.22,.981],58121:[.717,.137,.727]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(185);e.fraktur=n.AddCSS(i.fraktur,{32:{c:" "},33:{c:"!"},38:{c:"&"},40:{c:"("},41:{c:")"},42:{c:"*"},43:{c:"+"},44:{c:","},45:{c:"-"},46:{c:"."},47:{c:"/"},48:{c:"0"},49:{c:"1"},50:{c:"2"},51:{c:"3"},52:{c:"4"},53:{c:"5"},54:{c:"6"},55:{c:"7"},56:{c:"8"},57:{c:"9"},58:{c:":"},59:{c:";"},61:{c:"="},63:{c:"?"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},91:{c:"["},93:{c:"]"},94:{c:"^"},97:{c:"a"},98:{c:"b"},99:{c:"c"},100:{c:"d"},101:{c:"e"},102:{c:"f"},103:{c:"g"},104:{c:"h"},105:{c:"i"},106:{c:"j"},107:{c:"k"},108:{c:"l"},109:{c:"m"},110:{c:"n"},111:{c:"o"},112:{c:"p"},113:{c:"q"},114:{c:"r"},115:{c:"s"},116:{c:"t"},117:{c:"u"},118:{c:"v"},119:{c:"w"},120:{c:"x"},121:{c:"y"},122:{c:"z"},913:{c:"A",f:""},914:{c:"B",f:""},917:{c:"E",f:""},918:{c:"Z",f:""},919:{c:"H",f:""},921:{c:"I",f:""},922:{c:"K",f:""},924:{c:"M",f:""},925:{c:"N",f:""},927:{c:"O",f:""},929:{c:"P",f:""},930:{c:"\\398",f:""},932:{c:"T",f:""},935:{c:"X",f:""},978:{c:"\\3A5",f:""},988:{c:"F",f:""},8260:{c:"/"},8460:{c:"H",f:"FR"},8465:{c:"I",f:"FR"},8476:{c:"R",f:"FR"},8488:{c:"Z",f:"FR"},8493:{c:"C",f:"FR"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.fraktur={32:[0,0,.25],33:[.689,.012,.296],34:[.695,-.432,.215],38:[.698,.011,.738],39:[.695,-.436,.212],40:[.737,.186,.389],41:[.735,.187,.389],42:[.692,-.449,.278],43:[.598,.082,.756],44:[.107,.191,.278],45:[.275,-.236,.756],46:[.102,.015,.278],47:[.721,.182,.502],48:[.492,.013,.502],49:[.468,0,.502],50:[.474,0,.502],51:[.473,.182,.502],52:[.476,.191,.502],53:[.458,.184,.502],54:[.7,.013,.502],55:[.468,.181,.502],56:[.705,.01,.502],57:[.469,.182,.502],58:[.457,.012,.216],59:[.458,.189,.216],61:[.368,-.132,.756],63:[.693,.011,.362],65:[.696,.026,.718],66:[.691,.027,.884],67:[.685,.024,.613],68:[.685,.027,.832],69:[.685,.024,.663],70:[.686,.153,.611],71:[.69,.026,.785],72:[.666,.133,.72],73:[.686,.026,.554],74:[.686,.139,.552],75:[.68,.027,.668],76:[.686,.026,.666],77:[.692,.027,1.05],78:[.686,.025,.832],79:[.729,.027,.827],80:[.692,.218,.828],81:[.729,.069,.827],82:[.686,.026,.828],83:[.692,.027,.829],84:[.701,.027,.669],85:[.697,.027,.646],86:[.686,.026,.831],87:[.686,.027,1.046],88:[.688,.027,.719],89:[.686,.218,.833],90:[.729,.139,.602],91:[.74,.13,.278],93:[.738,.131,.278],94:[.734,-.452,.5],97:[.47,.035,.5],98:[.685,.031,.513],99:[.466,.029,.389],100:[.609,.033,.499],101:[.467,.03,.401],102:[.681,.221,.326],103:[.47,.209,.504],104:[.688,.205,.521],105:[.673,.02,.279],106:[.672,.208,.281],107:[.689,.025,.389],108:[.685,.02,.28],109:[.475,.026,.767],110:[.475,.022,.527],111:[.48,.028,.489],112:[.541,.212,.5],113:[.479,.219,.489],114:[.474,.021,.389],115:[.478,.029,.443],116:[.64,.02,.333],117:[.474,.023,.517],118:[.53,.028,.512],119:[.532,.028,.774],120:[.472,.188,.389],121:[.528,.218,.499],122:[.471,.214,.391],160:[0,0,.25],913:[.716,0,.75],914:[.683,0,.708],917:[.68,0,.681],918:[.683,0,.611],919:[.683,0,.75],921:[.683,0,.361],922:[.683,0,.778],924:[.683,0,.917],925:[.683,0,.75],927:[.705,.022,.778],929:[.683,0,.681],930:[.705,.022,.778],932:[.677,0,.722],935:[.683,0,.75],978:[.705,0,.778],988:[.68,0,.653],8216:[.708,-.41,.215],8217:[.692,-.395,.215],8260:[.721,.182,.502],8460:[.666,.133,.72],8465:[.686,.026,.554],8476:[.686,.026,.828],8488:[.729,.139,.602],8493:[.685,.024,.613],58112:[.683,.032,.497],58113:[.616,.03,.498],58114:[.68,.215,.333],58115:[.679,.224,.329],58116:[.471,.214,.503],58117:[.686,.02,.333],58118:[.577,.021,.334],58119:[.475,.022,.501]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(187);e.italic=n.AddCSS(i.italic,{32:{c:" "},33:{c:"!"},35:{c:"#"},37:{c:"%"},38:{c:"&"},40:{c:"("},41:{c:")"},42:{c:"*"},43:{c:"+"},44:{c:","},45:{c:"-"},46:{c:"."},47:{c:"/"},48:{c:"0"},49:{c:"1"},50:{c:"2"},51:{c:"3"},52:{c:"4"},53:{c:"5"},54:{c:"6"},55:{c:"7"},56:{c:"8"},57:{c:"9"},58:{c:":"},59:{c:";"},61:{c:"="},63:{c:"?"},64:{c:"@"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},91:{c:"["},93:{c:"]"},94:{c:"^"},95:{c:"_"},97:{c:"a"},98:{c:"b"},99:{c:"c"},100:{c:"d"},101:{c:"e"},102:{c:"f"},103:{c:"g"},104:{c:"h"},105:{c:"i"},106:{c:"j"},107:{c:"k"},108:{c:"l"},109:{c:"m"},110:{c:"n"},111:{c:"o"},112:{c:"p"},113:{c:"q"},114:{c:"r"},115:{c:"s"},116:{c:"t"},117:{c:"u"},118:{c:"v"},119:{c:"w"},120:{c:"x"},121:{c:"y"},122:{c:"z"},126:{c:"~"},913:{c:"A"},914:{c:"B"},917:{c:"E"},918:{c:"Z"},919:{c:"H"},921:{c:"I"},922:{c:"K"},924:{c:"M"},925:{c:"N"},927:{c:"O"},929:{c:"P"},930:{c:"\\398"},932:{c:"T"},935:{c:"X"},978:{c:"\\3A5"},988:{c:"F"},8213:{c:"\\2014"},8215:{c:"_"},8260:{c:"/"},8462:{c:"h",f:"I"},8710:{c:"\\394"},10744:{c:"/",f:"I"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.italic={32:[0,0,.25],33:[.716,0,.307,{ic:.073}],34:[.694,-.379,.514],35:[.694,.194,.818],37:[.75,.056,.818],38:[.716,.022,.767],39:[.694,-.379,.307,{ic:.07}],40:[.75,.25,.409,{ic:.108}],41:[.75,.25,.409],42:[.75,-.32,.511,{ic:.073}],43:[.557,.057,.767],44:[.121,.194,.307],45:[.251,-.18,.358],46:[.121,0,.307],47:[.716,.215,.778],48:[.665,.021,.511,{ic:.051}],49:[.666,0,.511],50:[.666,.022,.511],51:[.666,.022,.511,{ic:.051}],52:[.666,.194,.511],53:[.666,.022,.511,{ic:.056}],54:[.665,.022,.511,{ic:.054}],55:[.666,.022,.511,{ic:.123}],56:[.666,.021,.511],57:[.666,.022,.511],58:[.431,0,.307],59:[.431,.194,.307],61:[.367,-.133,.767],63:[.716,0,.511],64:[.705,.011,.767],65:[.716,0,.75,{sk:.139}],66:[.683,0,.759,{sk:.0833}],67:[.705,.022,.715,{sk:.0833}],68:[.683,0,.828,{sk:.0556}],69:[.68,0,.738,{sk:.0833}],70:[.68,0,.643,{ic:.106,sk:.0833}],71:[.705,.022,.786,{sk:.0833}],72:[.683,0,.831,{ic:.057,sk:.0556}],73:[.683,0,.44,{ic:.064,sk:.111}],74:[.683,.022,.555,{ic:.078,sk:.167}],75:[.683,0,.849,{sk:.0556}],76:[.683,0,.681,{sk:.0278}],77:[.683,0,.97,{ic:.081,sk:.0833}],78:[.683,0,.803,{ic:.085,sk:.0833}],79:[.704,.022,.763,{sk:.0833}],80:[.683,0,.642,{ic:.109,sk:.0833}],81:[.704,.194,.791,{sk:.0833}],82:[.683,.021,.759,{sk:.0833}],83:[.705,.022,.613,{sk:.0833}],84:[.677,0,.584,{ic:.12,sk:.0833}],85:[.683,.022,.683,{ic:.084,sk:.0278}],86:[.683,.022,.583,{ic:.186}],87:[.683,.022,.944,{ic:.104}],88:[.683,0,.828,{sk:.0833}],89:[.683,0,.581,{ic:.182}],90:[.683,0,.683,{sk:.0833}],91:[.75,.25,.307,{ic:.139}],93:[.75,.25,.307,{ic:.052}],94:[.694,-.527,.511],95:[-.025,.062,.511],97:[.441,.01,.529],98:[.694,.011,.429],99:[.442,.011,.433,{sk:.0556}],100:[.694,.01,.52,{sk:.167}],101:[.442,.011,.466,{sk:.0556}],102:[.705,.205,.49,{ic:.06,sk:.167}],103:[.442,.205,.477,{sk:.0278}],104:[.694,.011,.576,{sk:-.0278}],105:[.661,.011,.345],106:[.661,.204,.412],107:[.694,.011,.521],108:[.694,.011,.298,{sk:.0833}],109:[.442,.011,.878],110:[.442,.011,.6],111:[.441,.011,.485,{sk:.0556}],112:[.442,.194,.503,{sk:.0833}],113:[.442,.194,.446,{sk:.0833}],114:[.442,.011,.451,{sk:.0556}],115:[.442,.01,.469,{sk:.0556}],116:[.626,.011,.361,{sk:.0833}],117:[.442,.011,.572,{sk:.0278}],118:[.443,.011,.485,{sk:.0278}],119:[.443,.011,.716,{sk:.0833}],120:[.442,.011,.572,{sk:.0278}],121:[.442,.205,.49,{sk:.0556}],122:[.442,.011,.465,{sk:.0556}],126:[.318,-.208,.511,{ic:.06}],160:[0,0,.25],163:[.714,.011,.769],305:[.441,.01,.307],567:[.442,.204,.332],768:[.697,-.5,0],769:[.697,-.5,0],770:[.694,-.527,0],771:[.668,-.558,0,{ic:.06}],772:[.589,-.544,0,{ic:.054}],774:[.694,-.515,0,{ic:.062}],775:[.669,-.548,0],776:[.669,-.554,0],778:[.716,-.542,0],779:[.697,-.503,0,{ic:.065}],780:[.638,-.502,0],913:[.716,0,.75,{sk:.139}],914:[.683,0,.759,{sk:.0833}],915:[.68,0,.615,{ic:.106,sk:.0833}],916:[.716,0,.833,{sk:.167}],917:[.68,0,.738,{sk:.0833}],918:[.683,0,.683,{sk:.0833}],919:[.683,0,.831,{ic:.057,sk:.0556}],920:[.704,.022,.763,{sk:.0833}],921:[.683,0,.44,{ic:.064,sk:.111}],922:[.683,0,.849,{sk:.0556}],923:[.716,0,.694,{sk:.167}],924:[.683,0,.97,{ic:.081,sk:.0833}],925:[.683,0,.803,{ic:.085,sk:.0833}],926:[.677,0,.742,{sk:.0833}],927:[.704,.022,.763,{sk:.0833}],928:[.68,0,.831,{ic:.056,sk:.0556}],929:[.683,0,.642,{ic:.109,sk:.0833}],930:[.704,.022,.763,{sk:.0833}],931:[.683,0,.78,{sk:.0833}],932:[.677,0,.584,{ic:.12,sk:.0833}],933:[.705,0,.583,{ic:.117,sk:.0556}],934:[.683,0,.667,{sk:.0833}],935:[.683,0,.828,{sk:.0833}],936:[.683,0,.612,{ic:.08,sk:.0556}],937:[.704,0,.772,{sk:.0833}],945:[.442,.011,.64,{sk:.0278}],946:[.705,.194,.566,{sk:.0833}],947:[.441,.216,.518],948:[.717,.01,.444,{sk:.0556}],949:[.452,.022,.466,{sk:.0833}],950:[.704,.204,.438,{sk:.0833}],951:[.442,.216,.497,{sk:.0556}],952:[.705,.01,.469,{sk:.0833}],953:[.442,.01,.354,{sk:.0556}],954:[.442,.011,.576],955:[.694,.012,.583],956:[.442,.216,.603,{sk:.0278}],957:[.442,0,.494,{sk:.0278}],958:[.704,.205,.438,{sk:.111}],959:[.441,.011,.485,{sk:.0556}],960:[.431,.011,.57],961:[.442,.216,.517,{sk:.0833}],962:[.442,.107,.363,{sk:.0833}],963:[.431,.011,.571],964:[.431,.013,.437,{ic:.08,sk:.0278}],965:[.443,.01,.54,{sk:.0278}],966:[.442,.218,.654,{sk:.0833}],967:[.442,.204,.626,{sk:.0556}],968:[.694,.205,.651,{sk:.111}],969:[.443,.011,.622],977:[.705,.011,.591,{sk:.0833}],978:[.705,0,.583,{ic:.117,sk:.0556}],981:[.694,.205,.596,{sk:.0833}],982:[.431,.01,.828],988:[.68,0,.643,{ic:.106,sk:.0833}],1009:[.442,.194,.517,{sk:.0833}],1013:[.431,.011,.406,{sk:.0556}],8211:[.285,-.248,.511],8212:[.285,-.248,1.022],8213:[.285,-.248,1.022],8215:[-.025,.062,.511],8216:[.694,-.379,.307,{ic:.055}],8217:[.694,-.379,.307,{ic:.07}],8220:[.694,-.379,.514,{ic:.092}],8221:[.694,-.379,.514],8260:[.716,.215,.778],8462:[.694,.011,.576,{sk:-.0278}],8463:[.695,.013,.54],8710:[.716,0,.833,{sk:.167}],10744:[.716,.215,.778]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(189);e.largeop=n.AddCSS(i.largeop,{32:{c:" "},40:{c:"("},41:{c:")"},47:{c:"/"},91:{c:"["},93:{c:"]"},123:{c:"{"},125:{c:"}"},8260:{c:"/"},9001:{c:"\\27E8"},9002:{c:"\\27E9"},10072:{c:"\\2223"},10764:{c:"\\222C\\222C"},12296:{c:"\\27E8"},12297:{c:"\\27E9"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.largeop={32:[0,0,.25],40:[1.15,.649,.597],41:[1.15,.649,.597],47:[1.15,.649,.811],91:[1.15,.649,.472],92:[1.15,.649,.811],93:[1.15,.649,.472],123:[1.15,.649,.667],125:[1.15,.649,.667],160:[0,0,.25],710:[.772,-.565,1],732:[.75,-.611,1],770:[.772,-.565,0],771:[.75,-.611,0],8214:[.602,0,.778],8260:[1.15,.649,.811],8593:[.6,0,.667],8595:[.6,0,.667],8657:[.599,0,.778],8659:[.6,0,.778],8719:[.95,.45,1.278],8720:[.95,.45,1.278],8721:[.95,.45,1.444],8730:[1.15,.65,1],8739:[.627,.015,.333],8741:[.627,.015,.556],8747:[1.36,.862,.556,{ic:.388}],8748:[1.36,.862,1.084,{ic:.388}],8749:[1.36,.862,1.592,{ic:.388}],8750:[1.36,.862,.556,{ic:.388}],8896:[.95,.45,1.111],8897:[.95,.45,1.111],8898:[.949,.45,1.111],8899:[.95,.449,1.111],8968:[1.15,.649,.528],8969:[1.15,.649,.528],8970:[1.15,.649,.528],8971:[1.15,.649,.528],9001:[1.15,.649,.611],9002:[1.15,.649,.611],9168:[.602,0,.667],10072:[.627,.015,.333],10216:[1.15,.649,.611],10217:[1.15,.649,.611],10752:[.949,.449,1.511],10753:[.949,.449,1.511],10754:[.949,.449,1.511],10756:[.95,.449,1.111],10758:[.95,.45,1.111],10764:[1.36,.862,2.168,{ic:.388}],12296:[1.15,.649,.611],12297:[1.15,.649,.611]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(191);e.monospace=n.AddCSS(i.monospace,{32:{c:" "},33:{c:"!"},35:{c:"#"},36:{c:"$"},37:{c:"%"},38:{c:"&"},40:{c:"("},41:{c:")"},42:{c:"*"},43:{c:"+"},44:{c:","},45:{c:"-"},46:{c:"."},47:{c:"/"},48:{c:"0"},49:{c:"1"},50:{c:"2"},51:{c:"3"},52:{c:"4"},53:{c:"5"},54:{c:"6"},55:{c:"7"},56:{c:"8"},57:{c:"9"},58:{c:":"},59:{c:";"},60:{c:"<"},61:{c:"="},62:{c:">"},63:{c:"?"},64:{c:"@"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},91:{c:"["},93:{c:"]"},94:{c:"^"},95:{c:"_"},96:{c:"`"},97:{c:"a"},98:{c:"b"},99:{c:"c"},100:{c:"d"},101:{c:"e"},102:{c:"f"},103:{c:"g"},104:{c:"h"},105:{c:"i"},106:{c:"j"},107:{c:"k"},108:{c:"l"},109:{c:"m"},110:{c:"n"},111:{c:"o"},112:{c:"p"},113:{c:"q"},114:{c:"r"},115:{c:"s"},116:{c:"t"},117:{c:"u"},118:{c:"v"},119:{c:"w"},120:{c:"x"},121:{c:"y"},122:{c:"z"},123:{c:"{"},124:{c:"|"},125:{c:"}"},126:{c:"~"},697:{c:"\\2032"},913:{c:"A"},914:{c:"B"},917:{c:"E"},918:{c:"Z"},919:{c:"H"},921:{c:"I"},922:{c:"K"},924:{c:"M"},925:{c:"N"},927:{c:"O"},929:{c:"P"},930:{c:"\\398"},932:{c:"T"},935:{c:"X"},978:{c:"\\3A5"},988:{c:"F"},8215:{c:"_"},8243:{c:"\\2032\\2032"},8244:{c:"\\2032\\2032\\2032"},8260:{c:"/"},8279:{c:"\\2032\\2032\\2032\\2032"},8710:{c:"\\394"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.monospace={32:[0,0,.525],33:[.622,0,.525],34:[.623,-.333,.525],35:[.611,0,.525],36:[.694,.082,.525],37:[.694,.083,.525],38:[.622,.011,.525],39:[.611,-.287,.525],40:[.694,.082,.525],41:[.694,.082,.525],42:[.52,-.09,.525],43:[.531,-.081,.525],44:[.14,.139,.525],45:[.341,-.271,.525],46:[.14,0,.525],47:[.694,.083,.525],48:[.621,.01,.525],49:[.622,0,.525],50:[.622,0,.525],51:[.622,.011,.525],52:[.624,0,.525],53:[.611,.01,.525],54:[.622,.011,.525],55:[.627,.01,.525],56:[.621,.01,.525],57:[.622,.011,.525],58:[.431,0,.525],59:[.431,.139,.525],60:[.557,-.055,.525],61:[.417,-.195,.525],62:[.557,-.055,.525],63:[.617,0,.525],64:[.617,.006,.525],65:[.623,0,.525],66:[.611,0,.525],67:[.622,.011,.525],68:[.611,0,.525],69:[.611,0,.525],70:[.611,0,.525],71:[.622,.011,.525],72:[.611,0,.525],73:[.611,0,.525],74:[.611,.011,.525],75:[.611,0,.525],76:[.611,0,.525],77:[.611,0,.525],78:[.611,0,.525],79:[.621,.01,.525],80:[.611,0,.525],81:[.621,.138,.525],82:[.611,.011,.525],83:[.622,.011,.525],84:[.611,0,.525],85:[.611,.011,.525],86:[.611,.007,.525],87:[.611,.007,.525],88:[.611,0,.525],89:[.611,0,.525],90:[.611,0,.525],91:[.694,.082,.525],92:[.694,.083,.525],93:[.694,.082,.525],94:[.611,-.46,.525],95:[-.025,.095,.525],96:[.681,-.357,.525],97:[.439,.006,.525],98:[.611,.006,.525],99:[.44,.006,.525],100:[.611,.006,.525],101:[.44,.006,.525],102:[.617,0,.525],103:[.442,.229,.525],104:[.611,0,.525],105:[.612,0,.525],106:[.612,.228,.525],107:[.611,0,.525],108:[.611,0,.525],109:[.436,0,.525],110:[.436,0,.525],111:[.44,.006,.525],112:[.437,.221,.525],113:[.437,.221,.525],114:[.437,0,.525],115:[.44,.006,.525],116:[.554,.006,.525],117:[.431,.005,.525],118:[.431,0,.525],119:[.431,0,.525],120:[.431,0,.525],121:[.431,.228,.525],122:[.431,0,.525],123:[.694,.083,.525],124:[.694,.082,.525],125:[.694,.083,.525],126:[.611,-.466,.525],127:[.612,-.519,.525],160:[0,0,.525],305:[.431,0,.525],567:[.431,.228,.525],697:[.623,-.334,.525],768:[.611,-.485,0],769:[.611,-.485,0],770:[.611,-.46,0],771:[.611,-.466,0],772:[.577,-.5,0],774:[.611,-.504,0],776:[.612,-.519,0],778:[.619,-.499,0],780:[.577,-.449,0],913:[.623,0,.525],914:[.611,0,.525],915:[.611,0,.525],916:[.623,0,.525],917:[.611,0,.525],918:[.611,0,.525],919:[.611,0,.525],920:[.621,.01,.525],921:[.611,0,.525],922:[.611,0,.525],923:[.623,0,.525],924:[.611,0,.525],925:[.611,0,.525],926:[.611,0,.525],927:[.621,.01,.525],928:[.611,0,.525],929:[.611,0,.525],930:[.621,.01,.525],931:[.611,0,.525],932:[.611,0,.525],933:[.622,0,.525],934:[.611,0,.525],935:[.611,0,.525],936:[.611,0,.525],937:[.622,0,.525],978:[.622,0,.525],988:[.611,0,.525],8215:[-.025,.095,.525],8242:[.623,-.334,.525],8243:[.623,0,1.05],8244:[.623,0,1.575],8260:[.694,.083,.525],8279:[.623,0,2.1],8710:[.623,0,.525]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(193);e.normal=n.AddCSS(i.normal,{32:{c:" "},33:{c:"!"},35:{c:"#"},36:{c:"$"},37:{c:"%"},38:{c:"&"},40:{c:"("},41:{c:")"},42:{c:"*"},43:{c:"+"},44:{c:","},45:{c:"-"},46:{c:"."},47:{c:"/"},48:{c:"0"},49:{c:"1"},50:{c:"2"},51:{c:"3"},52:{c:"4"},53:{c:"5"},54:{c:"6"},55:{c:"7"},56:{c:"8"},57:{c:"9"},58:{c:":"},59:{c:";"},60:{c:"<"},61:{c:"="},62:{c:">"},63:{c:"?"},64:{c:"@"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},91:{c:"["},93:{c:"]"},94:{c:"^"},95:{c:"_"},96:{c:"`"},97:{c:"a"},98:{c:"b"},99:{c:"c"},100:{c:"d"},101:{c:"e"},102:{c:"f"},103:{c:"g"},104:{c:"h"},105:{c:"i"},106:{c:"j"},107:{c:"k"},108:{c:"l"},109:{c:"m"},110:{c:"n"},111:{c:"o"},112:{c:"p"},113:{c:"q"},114:{c:"r"},115:{c:"s"},116:{c:"t"},117:{c:"u"},118:{c:"v"},119:{c:"w"},120:{c:"x"},121:{c:"y"},122:{c:"z"},123:{c:"{"},124:{c:"|"},125:{c:"}"},126:{c:"~"},163:{f:"MI"},183:{c:"\\22C5"},697:{c:"\\2032"},913:{c:"A"},914:{c:"B"},917:{c:"E"},918:{c:"Z"},919:{c:"H"},921:{c:"I"},922:{c:"K"},924:{c:"M"},925:{c:"N"},927:{c:"O"},929:{c:"P"},930:{c:"\\398"},932:{c:"T"},935:{c:"X"},978:{c:"\\3A5"},988:{c:"F"},1014:{c:"\\220D"},8192:{c:""},8193:{c:""},8194:{c:""},8195:{c:""},8196:{c:""},8197:{c:""},8198:{c:""},8201:{c:""},8202:{c:""},8203:{c:""},8204:{c:""},8213:{c:"\\2014"},8214:{c:"\\2225"},8215:{c:"_"},8226:{c:"\\2219"},8243:{c:"\\2032\\2032"},8244:{c:"\\2032\\2032\\2032"},8246:{c:"\\2035\\2035"},8247:{c:"\\2035\\2035\\2035"},8254:{c:"\\2C9"},8260:{c:"/"},8279:{c:"\\2032\\2032\\2032\\2032"},8289:{c:""},8290:{c:""},8291:{c:""},8292:{c:""},8407:{c:"\\2192",f:"V"},8450:{c:"C",f:"A"},8459:{c:"H",f:"SC"},8460:{c:"H",f:"FR"},8461:{c:"H",f:"A"},8462:{c:"h",f:"I"},8463:{f:""},8464:{c:"J",f:"SC"},8465:{c:"I",f:"FR"},8466:{c:"L",f:"SC"},8469:{c:"N",f:"A"},8473:{c:"P",f:"A"},8474:{c:"Q",f:"A"},8475:{c:"R",f:"SC"},8476:{c:"R",f:"FR"},8477:{c:"R",f:"A"},8484:{c:"Z",f:"A"},8486:{c:"\\3A9",f:""},8488:{c:"Z",f:"FR"},8492:{c:"B",f:"SC"},8493:{c:"C",f:"FR"},8496:{c:"E",f:"SC"},8497:{c:"F",f:"SC"},8499:{c:"M",f:"SC"},8708:{c:"\\2203\\338"},8710:{c:"\\394"},8716:{c:"\\220B\\338"},8726:{f:""},8772:{c:"\\2243\\338"},8775:{c:"\\2246"},8777:{c:"\\2248\\338"},8802:{c:"\\2261\\338"},8813:{c:"\\224D\\338"},8820:{c:"\\2272\\338"},8821:{c:"\\2273\\338"},8824:{c:"\\2276\\338"},8825:{c:"\\2277\\338"},8836:{c:"\\2282\\338"},8837:{c:"\\2283\\338"},8930:{c:"\\2291\\338"},8931:{c:"\\2292\\338"},8965:{c:"\\22BC"},8966:{c:"\\2A5E"},8988:{c:"\\250C"},8989:{c:"\\2510"},8990:{c:"\\2514"},8991:{c:"\\2518"},9001:{c:"\\27E8"},9002:{c:"\\27E9"},9642:{c:"\\25A0"},9652:{c:"\\25B2"},9653:{c:"\\25B3"},9656:{c:"\\25B6"},9662:{c:"\\25BC"},9663:{c:"\\25BD"},9666:{c:"\\25C0"},9723:{c:"\\25A1"},9724:{c:"\\25A0"},10072:{c:"\\2223"},10744:{c:"/",f:"I"},10764:{c:"\\222C\\222C"},10799:{c:"\\D7"},12296:{c:"\\27E8"},12297:{c:"\\27E9"},119808:{c:"A",f:"B"},119809:{c:"B",f:"B"},119810:{c:"C",f:"B"},119811:{c:"D",f:"B"},119812:{c:"E",f:"B"},119813:{c:"F",f:"B"},119814:{c:"G",f:"B"},119815:{c:"H",f:"B"},119816:{c:"I",f:"B"},119817:{c:"J",f:"B"},119818:{c:"K",f:"B"},119819:{c:"L",f:"B"},119820:{c:"M",f:"B"},119821:{c:"N",f:"B"},119822:{c:"O",f:"B"},119823:{c:"P",f:"B"},119824:{c:"Q",f:"B"},119825:{c:"R",f:"B"},119826:{c:"S",f:"B"},119827:{c:"T",f:"B"},119828:{c:"U",f:"B"},119829:{c:"V",f:"B"},119830:{c:"W",f:"B"},119831:{c:"X",f:"B"},119832:{c:"Y",f:"B"},119833:{c:"Z",f:"B"},119834:{c:"a",f:"B"},119835:{c:"b",f:"B"},119836:{c:"c",f:"B"},119837:{c:"d",f:"B"},119838:{c:"e",f:"B"},119839:{c:"f",f:"B"},119840:{c:"g",f:"B"},119841:{c:"h",f:"B"},119842:{c:"i",f:"B"},119843:{c:"j",f:"B"},119844:{c:"k",f:"B"},119845:{c:"l",f:"B"},119846:{c:"m",f:"B"},119847:{c:"n",f:"B"},119848:{c:"o",f:"B"},119849:{c:"p",f:"B"},119850:{c:"q",f:"B"},119851:{c:"r",f:"B"},119852:{c:"s",f:"B"},119853:{c:"t",f:"B"},119854:{c:"u",f:"B"},119855:{c:"v",f:"B"},119856:{c:"w",f:"B"},119857:{c:"x",f:"B"},119858:{c:"y",f:"B"},119859:{c:"z",f:"B"},119860:{c:"A",f:"I"},119861:{c:"B",f:"I"},119862:{c:"C",f:"I"},119863:{c:"D",f:"I"},119864:{c:"E",f:"I"},119865:{c:"F",f:"I"},119866:{c:"G",f:"I"},119867:{c:"H",f:"I"},119868:{c:"I",f:"I"},119869:{c:"J",f:"I"},119870:{c:"K",f:"I"},119871:{c:"L",f:"I"},119872:{c:"M",f:"I"},119873:{c:"N",f:"I"},119874:{c:"O",f:"I"},119875:{c:"P",f:"I"},119876:{c:"Q",f:"I"},119877:{c:"R",f:"I"},119878:{c:"S",f:"I"},119879:{c:"T",f:"I"},119880:{c:"U",f:"I"},119881:{c:"V",f:"I"},119882:{c:"W",f:"I"},119883:{c:"X",f:"I"},119884:{c:"Y",f:"I"},119885:{c:"Z",f:"I"},119886:{c:"a",f:"I"},119887:{c:"b",f:"I"},119888:{c:"c",f:"I"},119889:{c:"d",f:"I"},119890:{c:"e",f:"I"},119891:{c:"f",f:"I"},119892:{c:"g",f:"I"},119893:{c:"h",f:"I"},119894:{c:"i",f:"I"},119895:{c:"j",f:"I"},119896:{c:"k",f:"I"},119897:{c:"l",f:"I"},119898:{c:"m",f:"I"},119899:{c:"n",f:"I"},119900:{c:"o",f:"I"},119901:{c:"p",f:"I"},119902:{c:"q",f:"I"},119903:{c:"r",f:"I"},119904:{c:"s",f:"I"},119905:{c:"t",f:"I"},119906:{c:"u",f:"I"},119907:{c:"v",f:"I"},119908:{c:"w",f:"I"},119909:{c:"x",f:"I"},119910:{c:"y",f:"I"},119911:{c:"z",f:"I"},119912:{c:"A",f:"BI"},119913:{c:"B",f:"BI"},119914:{c:"C",f:"BI"},119915:{c:"D",f:"BI"},119916:{c:"E",f:"BI"},119917:{c:"F",f:"BI"},119918:{c:"G",f:"BI"},119919:{c:"H",f:"BI"},119920:{c:"I",f:"BI"},119921:{c:"J",f:"BI"},119922:{c:"K",f:"BI"},119923:{c:"L",f:"BI"},119924:{c:"M",f:"BI"},119925:{c:"N",f:"BI"},119926:{c:"O",f:"BI"},119927:{c:"P",f:"BI"},119928:{c:"Q",f:"BI"},119929:{c:"R",f:"BI"},119930:{c:"S",f:"BI"},119931:{c:"T",f:"BI"},119932:{c:"U",f:"BI"},119933:{c:"V",f:"BI"},119934:{c:"W",f:"BI"},119935:{c:"X",f:"BI"},119936:{c:"Y",f:"BI"},119937:{c:"Z",f:"BI"},119938:{c:"a",f:"BI"},119939:{c:"b",f:"BI"},119940:{c:"c",f:"BI"},119941:{c:"d",f:"BI"},119942:{c:"e",f:"BI"},119943:{c:"f",f:"BI"},119944:{c:"g",f:"BI"},119945:{c:"h",f:"BI"},119946:{c:"i",f:"BI"},119947:{c:"j",f:"BI"},119948:{c:"k",f:"BI"},119949:{c:"l",f:"BI"},119950:{c:"m",f:"BI"},119951:{c:"n",f:"BI"},119952:{c:"o",f:"BI"},119953:{c:"p",f:"BI"},119954:{c:"q",f:"BI"},119955:{c:"r",f:"BI"},119956:{c:"s",f:"BI"},119957:{c:"t",f:"BI"},119958:{c:"u",f:"BI"},119959:{c:"v",f:"BI"},119960:{c:"w",f:"BI"},119961:{c:"x",f:"BI"},119962:{c:"y",f:"BI"},119963:{c:"z",f:"BI"},119964:{c:"A",f:"SC"},119965:{c:"B",f:"SC"},119966:{c:"C",f:"SC"},119967:{c:"D",f:"SC"},119968:{c:"E",f:"SC"},119969:{c:"F",f:"SC"},119970:{c:"G",f:"SC"},119971:{c:"H",f:"SC"},119972:{c:"I",f:"SC"},119973:{c:"J",f:"SC"},119974:{c:"K",f:"SC"},119975:{c:"L",f:"SC"},119976:{c:"M",f:"SC"},119977:{c:"N",f:"SC"},119978:{c:"O",f:"SC"},119979:{c:"P",f:"SC"},119980:{c:"Q",f:"SC"},119981:{c:"R",f:"SC"},119982:{c:"S",f:"SC"},119983:{c:"T",f:"SC"},119984:{c:"U",f:"SC"},119985:{c:"V",f:"SC"},119986:{c:"W",f:"SC"},119987:{c:"X",f:"SC"},119988:{c:"Y",f:"SC"},119989:{c:"Z",f:"SC"},119990:{c:"a",f:"I"},119991:{c:"b",f:"I"},119992:{c:"c",f:"I"},119993:{c:"d",f:"I"},119994:{c:"e",f:"I"},119995:{c:"f",f:"I"},119996:{c:"g",f:"I"},119997:{c:"h",f:"I"},119998:{c:"i",f:"I"},119999:{c:"j",f:"I"},12e4:{c:"k",f:"I"},120001:{c:"l",f:"I"},120002:{c:"m",f:"I"},120003:{c:"n",f:"I"},120004:{c:"o",f:"I"},120005:{c:"p",f:"I"},120006:{c:"q",f:"I"},120007:{c:"r",f:"I"},120008:{c:"s",f:"I"},120009:{c:"t",f:"I"},120010:{c:"u",f:"I"},120011:{c:"v",f:"I"},120012:{c:"w",f:"I"},120013:{c:"x",f:"I"},120014:{c:"y",f:"I"},120015:{c:"z",f:"I"},120016:{c:"A",f:"SC"},120017:{c:"B",f:"SC"},120018:{c:"C",f:"SC"},120019:{c:"D",f:"SC"},120020:{c:"E",f:"SC"},120021:{c:"F",f:"SC"},120022:{c:"G",f:"SC"},120023:{c:"H",f:"SC"},120024:{c:"I",f:"SC"},120025:{c:"J",f:"SC"},120026:{c:"K",f:"SC"},120027:{c:"L",f:"SC"},120028:{c:"M",f:"SC"},120029:{c:"N",f:"SC"},120030:{c:"O",f:"SC"},120031:{c:"P",f:"SC"},120032:{c:"Q",f:"SC"},120033:{c:"R",f:"SC"},120034:{c:"S",f:"SC"},120035:{c:"T",f:"SC"},120036:{c:"U",f:"SC"},120037:{c:"V",f:"SC"},120038:{c:"W",f:"SC"},120039:{c:"X",f:"SC"},120040:{c:"Y",f:"SC"},120041:{c:"Z",f:"SC"},120042:{c:"a",f:"BI"},120043:{c:"b",f:"BI"},120044:{c:"c",f:"BI"},120045:{c:"d",f:"BI"},120046:{c:"e",f:"BI"},120047:{c:"f",f:"BI"},120048:{c:"g",f:"BI"},120049:{c:"h",f:"BI"},120050:{c:"i",f:"BI"},120051:{c:"j",f:"BI"},120052:{c:"k",f:"BI"},120053:{c:"l",f:"BI"},120054:{c:"m",f:"BI"},120055:{c:"n",f:"BI"},120056:{c:"o",f:"BI"},120057:{c:"p",f:"BI"},120058:{c:"q",f:"BI"},120059:{c:"r",f:"BI"},120060:{c:"s",f:"BI"},120061:{c:"t",f:"BI"},120062:{c:"u",f:"BI"},120063:{c:"v",f:"BI"},120064:{c:"w",f:"BI"},120065:{c:"x",f:"BI"},120066:{c:"y",f:"BI"},120067:{c:"z",f:"BI"},120068:{c:"A",f:"FR"},120069:{c:"B",f:"FR"},120070:{c:"C",f:"FR"},120071:{c:"D",f:"FR"},120072:{c:"E",f:"FR"},120073:{c:"F",f:"FR"},120074:{c:"G",f:"FR"},120075:{c:"H",f:"FR"},120076:{c:"I",f:"FR"},120077:{c:"J",f:"FR"},120078:{c:"K",f:"FR"},120079:{c:"L",f:"FR"},120080:{c:"M",f:"FR"},120081:{c:"N",f:"FR"},120082:{c:"O",f:"FR"},120083:{c:"P",f:"FR"},120084:{c:"Q",f:"FR"},120085:{c:"R",f:"FR"},120086:{c:"S",f:"FR"},120087:{c:"T",f:"FR"},120088:{c:"U",f:"FR"},120089:{c:"V",f:"FR"},120090:{c:"W",f:"FR"},120091:{c:"X",f:"FR"},120092:{c:"Y",f:"FR"},120093:{c:"Z",f:"FR"},120094:{c:"a",f:"FR"},120095:{c:"b",f:"FR"},120096:{c:"c",f:"FR"},120097:{c:"d",f:"FR"},120098:{c:"e",f:"FR"},120099:{c:"f",f:"FR"},120100:{c:"g",f:"FR"},120101:{c:"h",f:"FR"},120102:{c:"i",f:"FR"},120103:{c:"j",f:"FR"},120104:{c:"k",f:"FR"},120105:{c:"l",f:"FR"},120106:{c:"m",f:"FR"},120107:{c:"n",f:"FR"},120108:{c:"o",f:"FR"},120109:{c:"p",f:"FR"},120110:{c:"q",f:"FR"},120111:{c:"r",f:"FR"},120112:{c:"s",f:"FR"},120113:{c:"t",f:"FR"},120114:{c:"u",f:"FR"},120115:{c:"v",f:"FR"},120116:{c:"w",f:"FR"},120117:{c:"x",f:"FR"},120118:{c:"y",f:"FR"},120119:{c:"z",f:"FR"},120120:{c:"A",f:"A"},120121:{c:"B",f:"A"},120122:{c:"C",f:"A"},120123:{c:"D",f:"A"},120124:{c:"E",f:"A"},120125:{c:"F",f:"A"},120126:{c:"G",f:"A"},120127:{c:"H",f:"A"},120128:{c:"I",f:"A"},120129:{c:"J",f:"A"},120130:{c:"K",f:"A"},120131:{c:"L",f:"A"},120132:{c:"M",f:"A"},120133:{c:"N",f:"A"},120134:{c:"O",f:"A"},120135:{c:"P",f:"A"},120136:{c:"Q",f:"A"},120137:{c:"R",f:"A"},120138:{c:"S",f:"A"},120139:{c:"T",f:"A"},120140:{c:"U",f:"A"},120141:{c:"V",f:"A"},120142:{c:"W",f:"A"},120143:{c:"X",f:"A"},120144:{c:"Y",f:"A"},120145:{c:"Z",f:"A"},120146:{c:"a",f:"B"},120147:{c:"b",f:"B"},120148:{c:"c",f:"B"},120149:{c:"d",f:"B"},120150:{c:"e",f:"B"},120151:{c:"f",f:"B"},120152:{c:"g",f:"B"},120153:{c:"h",f:"B"},120154:{c:"i",f:"B"},120155:{c:"j",f:"B"},120156:{c:"k",f:"A"},120157:{c:"l",f:"B"},120158:{c:"m",f:"B"},120159:{c:"n",f:"B"},120160:{c:"o",f:"B"},120161:{c:"p",f:"B"},120162:{c:"q",f:"B"},120163:{c:"r",f:"B"},120164:{c:"s",f:"B"},120165:{c:"t",f:"B"},120166:{c:"u",f:"B"},120167:{c:"v",f:"B"},120168:{c:"w",f:"B"},120169:{c:"x",f:"B"},120170:{c:"y",f:"B"},120171:{c:"z",f:"B"},120172:{c:"A",f:"FR-B"},120173:{c:"B",f:"FR-B"},120174:{c:"C",f:"FR-B"},120175:{c:"D",f:"FR-B"},120176:{c:"E",f:"FR-B"},120177:{c:"F",f:"FR-B"},120178:{c:"G",f:"FR-B"},120179:{c:"H",f:"FR-B"},120180:{c:"I",f:"FR-B"},120181:{c:"J",f:"FR-B"},120182:{c:"K",f:"FR-B"},120183:{c:"L",f:"FR-B"},120184:{c:"M",f:"FR-B"},120185:{c:"N",f:"FR-B"},120186:{c:"O",f:"FR-B"},120187:{c:"P",f:"FR-B"},120188:{c:"Q",f:"FR-B"},120189:{c:"R",f:"FR-B"},120190:{c:"S",f:"FR-B"},120191:{c:"T",f:"FR-B"},120192:{c:"U",f:"FR-B"},120193:{c:"V",f:"FR-B"},120194:{c:"W",f:"FR-B"},120195:{c:"X",f:"FR-B"},120196:{c:"Y",f:"FR-B"},120197:{c:"Z",f:"FR-B"},120198:{c:"a",f:"FR-B"},120199:{c:"b",f:"FR-B"},120200:{c:"c",f:"FR-B"},120201:{c:"d",f:"FR-B"},120202:{c:"e",f:"FR-B"},120203:{c:"f",f:"FR-B"},120204:{c:"g",f:"FR-B"},120205:{c:"h",f:"FR-B"},120206:{c:"i",f:"FR-B"},120207:{c:"j",f:"FR-B"},120208:{c:"k",f:"FR-B"},120209:{c:"l",f:"FR-B"},120210:{c:"m",f:"FR-B"},120211:{c:"n",f:"FR-B"},120212:{c:"o",f:"FR-B"},120213:{c:"p",f:"FR-B"},120214:{c:"q",f:"FR-B"},120215:{c:"r",f:"FR-B"},120216:{c:"s",f:"FR-B"},120217:{c:"t",f:"FR-B"},120218:{c:"u",f:"FR-B"},120219:{c:"v",f:"FR-B"},120220:{c:"w",f:"FR-B"},120221:{c:"x",f:"FR-B"},120222:{c:"y",f:"FR-B"},120223:{c:"z",f:"FR-B"},120224:{c:"A",f:"SS"},120225:{c:"B",f:"SS"},120226:{c:"C",f:"SS"},120227:{c:"D",f:"SS"},120228:{c:"E",f:"SS"},120229:{c:"F",f:"SS"},120230:{c:"G",f:"SS"},120231:{c:"H",f:"SS"},120232:{c:"I",f:"SS"},120233:{c:"J",f:"SS"},120234:{c:"K",f:"SS"},120235:{c:"L",f:"SS"},120236:{c:"M",f:"SS"},120237:{c:"N",f:"SS"},120238:{c:"O",f:"SS"},120239:{c:"P",f:"SS"},120240:{c:"Q",f:"SS"},120241:{c:"R",f:"SS"},120242:{c:"S",f:"SS"},120243:{c:"T",f:"SS"},120244:{c:"U",f:"SS"},120245:{c:"V",f:"SS"},120246:{c:"W",f:"SS"},120247:{c:"X",f:"SS"},120248:{c:"Y",f:"SS"},120249:{c:"Z",f:"SS"},120250:{c:"a",f:"SS"},120251:{c:"b",f:"SS"},120252:{c:"c",f:"SS"},120253:{c:"d",f:"SS"},120254:{c:"e",f:"SS"},120255:{c:"f",f:"SS"},120256:{c:"g",f:"SS"},120257:{c:"h",f:"SS"},120258:{c:"i",f:"SS"},120259:{c:"j",f:"SS"},120260:{c:"k",f:"SS"},120261:{c:"l",f:"SS"},120262:{c:"m",f:"SS"},120263:{c:"n",f:"SS"},120264:{c:"o",f:"SS"},120265:{c:"p",f:"SS"},120266:{c:"q",f:"SS"},120267:{c:"r",f:"SS"},120268:{c:"s",f:"SS"},120269:{c:"t",f:"SS"},120270:{c:"u",f:"SS"},120271:{c:"v",f:"SS"},120272:{c:"w",f:"SS"},120273:{c:"x",f:"SS"},120274:{c:"y",f:"SS"},120275:{c:"z",f:"SS"},120276:{c:"A",f:"SS-B"},120277:{c:"B",f:"SS-B"},120278:{c:"C",f:"SS-B"},120279:{c:"D",f:"SS-B"},120280:{c:"E",f:"SS-B"},120281:{c:"F",f:"SS-B"},120282:{c:"G",f:"SS-B"},120283:{c:"H",f:"SS-B"},120284:{c:"I",f:"SS-B"},120285:{c:"J",f:"SS-B"},120286:{c:"K",f:"SS-B"},120287:{c:"L",f:"SS-B"},120288:{c:"M",f:"SS-B"},120289:{c:"N",f:"SS-B"},120290:{c:"O",f:"SS-B"},120291:{c:"P",f:"SS-B"},120292:{c:"Q",f:"SS-B"},120293:{c:"R",f:"SS-B"},120294:{c:"S",f:"SS-B"},120295:{c:"T",f:"SS-B"},120296:{c:"U",f:"SS-B"},120297:{c:"V",f:"SS-B"},120298:{c:"W",f:"SS-B"},120299:{c:"X",f:"SS-B"},120300:{c:"Y",f:"SS-B"},120301:{c:"Z",f:"SS-B"},120302:{c:"a",f:"SS-B"},120303:{c:"b",f:"SS-B"},120304:{c:"c",f:"SS-B"},120305:{c:"d",f:"SS-B"},120306:{c:"e",f:"SS-B"},120307:{c:"f",f:"SS-B"},120308:{c:"g",f:"SS-B"},120309:{c:"h",f:"SS-B"},120310:{c:"i",f:"SS-B"},120311:{c:"j",f:"SS-B"},120312:{c:"k",f:"SS-B"},120313:{c:"l",f:"SS-B"},120314:{c:"m",f:"SS-B"},120315:{c:"n",f:"SS-B"},120316:{c:"o",f:"SS-B"},120317:{c:"p",f:"SS-B"},120318:{c:"q",f:"SS-B"},120319:{c:"r",f:"SS-B"},120320:{c:"s",f:"SS-B"},120321:{c:"t",f:"SS-B"},120322:{c:"u",f:"SS-B"},120323:{c:"v",f:"SS-B"},120324:{c:"w",f:"SS-B"},120325:{c:"x",f:"SS-B"},120326:{c:"y",f:"SS-B"},120327:{c:"z",f:"SS-B"},120328:{c:"A",f:"SS-I"},120329:{c:"B",f:"SS-I"},120330:{c:"C",f:"SS-I"},120331:{c:"D",f:"SS-I"},120332:{c:"E",f:"SS-I"},120333:{c:"F",f:"SS-I"},120334:{c:"G",f:"SS-I"},120335:{c:"H",f:"SS-I"},120336:{c:"I",f:"SS-I"},120337:{c:"J",f:"SS-I"},120338:{c:"K",f:"SS-I"},120339:{c:"L",f:"SS-I"},120340:{c:"M",f:"SS-I"},120341:{c:"N",f:"SS-I"},120342:{c:"O",f:"SS-I"},120343:{c:"P",f:"SS-I"},120344:{c:"Q",f:"SS-I"},120345:{c:"R",f:"SS-I"},120346:{c:"S",f:"SS-I"},120347:{c:"T",f:"SS-I"},120348:{c:"U",f:"SS-I"},120349:{c:"V",f:"SS-I"},120350:{c:"W",f:"SS-I"},120351:{c:"X",f:"SS-I"},120352:{c:"Y",f:"SS-I"},120353:{c:"Z",f:"SS-I"},120354:{c:"a",f:"SS-I"},120355:{c:"b",f:"SS-I"},120356:{c:"c",f:"SS-I"},120357:{c:"d",f:"SS-I"},120358:{c:"e",f:"SS-I"},120359:{c:"f",f:"SS-I"},120360:{c:"g",f:"SS-I"},120361:{c:"h",f:"SS-I"},120362:{c:"i",f:"SS-I"},120363:{c:"j",f:"SS-I"},120364:{c:"k",f:"SS-I"},120365:{c:"l",f:"SS-I"},120366:{c:"m",f:"SS-I"},120367:{c:"n",f:"SS-I"},120368:{c:"o",f:"SS-I"},120369:{c:"p",f:"SS-I"},120370:{c:"q",f:"SS-I"},120371:{c:"r",f:"SS-I"},120372:{c:"s",f:"SS-I"},120373:{c:"t",f:"SS-I"},120374:{c:"u",f:"SS-I"},120375:{c:"v",f:"SS-I"},120376:{c:"w",f:"SS-I"},120377:{c:"x",f:"SS-I"},120378:{c:"y",f:"SS-I"},120379:{c:"z",f:"SS-I"},120380:{c:"A",f:"SS-I"},120381:{c:"B",f:"SS-I"},120382:{c:"C",f:"SS-I"},120383:{c:"D",f:"SS-I"},120384:{c:"E",f:"SS-I"},120385:{c:"F",f:"SS-I"},120386:{c:"G",f:"SS-I"},120387:{c:"H",f:"SS-I"},120388:{c:"I",f:"SS-I"},120389:{c:"J",f:"SS-I"},120390:{c:"K",f:"SS-I"},120391:{c:"L",f:"SS-I"},120392:{c:"M",f:"SS-I"},120393:{c:"N",f:"SS-I"},120394:{c:"O",f:"SS-I"},120395:{c:"P",f:"SS-I"},120396:{c:"Q",f:"SS-I"},120397:{c:"R",f:"SS-I"},120398:{c:"S",f:"SS-I"},120399:{c:"T",f:"SS-I"},120400:{c:"U",f:"SS-I"},120401:{c:"V",f:"SS-I"},120402:{c:"W",f:"SS-I"},120403:{c:"X",f:"SS-I"},120404:{c:"Y",f:"SS-I"},120405:{c:"Z",f:"SS-I"},120406:{c:"a",f:"SS-I"},120407:{c:"b",f:"SS-I"},120408:{c:"c",f:"SS-I"},120409:{c:"d",f:"SS-I"},120410:{c:"e",f:"SS-I"},120411:{c:"f",f:"SS-I"},120412:{c:"g",f:"SS-I"},120413:{c:"h",f:"SS-I"},120414:{c:"i",f:"SS-I"},120415:{c:"j",f:"SS-I"},120416:{c:"k",f:"SS-I"},120417:{c:"l",f:"SS-I"},120418:{c:"m",f:"SS-I"},120419:{c:"n",f:"SS-I"},120420:{c:"o",f:"SS-I"},120421:{c:"p",f:"SS-I"},120422:{c:"q",f:"SS-I"},120423:{c:"r",f:"SS-I"},120424:{c:"s",f:"SS-I"},120425:{c:"t",f:"SS-I"},120426:{c:"u",f:"SS-I"},120427:{c:"v",f:"SS-I"},120428:{c:"w",f:"SS-I"},120429:{c:"x",f:"SS-I"},120430:{c:"y",f:"SS-I"},120431:{c:"z",f:"SS-I"},120432:{c:"A",f:"T"},120433:{c:"B",f:"T"},120434:{c:"C",f:"T"},120435:{c:"D",f:"T"},120436:{c:"E",f:"T"},120437:{c:"F",f:"T"},120438:{c:"G",f:"T"},120439:{c:"H",f:"T"},120440:{c:"I",f:"T"},120441:{c:"J",f:"T"},120442:{c:"K",f:"T"},120443:{c:"L",f:"T"},120444:{c:"M",f:"T"},120445:{c:"N",f:"T"},120446:{c:"O",f:"T"},120447:{c:"P",f:"T"},120448:{c:"Q",f:"T"},120449:{c:"R",f:"T"},120450:{c:"S",f:"T"},120451:{c:"T",f:"T"},120452:{c:"U",f:"T"},120453:{c:"V",f:"T"},120454:{c:"W",f:"T"},120455:{c:"X",f:"T"},120456:{c:"Y",f:"T"},120457:{c:"Z",f:"T"},120458:{c:"a",f:"T"},120459:{c:"b",f:"T"},120460:{c:"c",f:"T"},120461:{c:"d",f:"T"},120462:{c:"e",f:"T"},120463:{c:"f",f:"T"},120464:{c:"g",f:"T"},120465:{c:"h",f:"T"},120466:{c:"i",f:"T"},120467:{c:"j",f:"T"},120468:{c:"k",f:"T"},120469:{c:"l",f:"T"},120470:{c:"m",f:"T"},120471:{c:"n",f:"T"},120472:{c:"o",f:"T"},120473:{c:"p",f:"T"},120474:{c:"q",f:"T"},120475:{c:"r",f:"T"},120476:{c:"s",f:"T"},120477:{c:"t",f:"T"},120478:{c:"u",f:"T"},120479:{c:"v",f:"T"},120480:{c:"w",f:"T"},120481:{c:"x",f:"T"},120482:{c:"y",f:"T"},120483:{c:"z",f:"T"},120484:{c:"\\131",f:"MI"},120485:{c:"\\237",f:"MI"},120488:{c:"A",f:"B"},120489:{c:"B",f:"B"},120490:{c:"\\393",f:"B"},120491:{c:"\\394",f:"B"},120492:{c:"E",f:"B"},120493:{c:"Z",f:"B"},120494:{c:"H",f:"B"},120495:{c:"\\398",f:"B"},120496:{c:"I",f:"B"},120497:{c:"K",f:"B"},120498:{c:"\\39B",f:"B"},120499:{c:"M",f:"B"},120500:{c:"N",f:"B"},120501:{c:"\\39E",f:"B"},120502:{c:"O",f:"B"},120503:{c:"\\3A0",f:"B"},120504:{c:"P",f:"B"},120505:{c:"\\398",f:"B"},120506:{c:"\\3A3",f:"B"},120507:{c:"T",f:"B"},120508:{c:"\\3A5",f:"B"},120509:{c:"\\3A6",f:"B"},120510:{c:"X",f:"B"},120511:{c:"\\3A8",f:"B"},120512:{c:"\\3A9",f:"B"},120513:{c:"\\2207",f:"B"},120514:{c:"\\3B1",f:"BI"},120515:{c:"\\3B2",f:"BI"},120516:{c:"\\3B3",f:"BI"},120517:{c:"\\3B4",f:"BI"},120518:{c:"\\3B5",f:"BI"},120519:{c:"\\3B6",f:"BI"},120520:{c:"\\3B7",f:"BI"},120521:{c:"\\3B8",f:"BI"},120522:{c:"\\3B9",f:"BI"},120523:{c:"\\3BA",f:"BI"},120524:{c:"\\3BB",f:"BI"},120525:{c:"\\3BC",f:"BI"},120526:{c:"\\3BD",f:"BI"},120527:{c:"\\3BE",f:"BI"},120528:{c:"\\3BF",f:"BI"},120529:{c:"\\3C0",f:"BI"},120530:{c:"\\3C1",f:"BI"},120531:{c:"\\3C2",f:"BI"},120532:{c:"\\3C3",f:"BI"},120533:{c:"\\3C4",f:"BI"},120534:{c:"\\3C5",f:"BI"},120535:{c:"\\3C6",f:"BI"},120536:{c:"\\3C7",f:"BI"},120537:{c:"\\3C8",f:"BI"},120538:{c:"\\3C9",f:"BI"},120539:{c:"\\2202",f:"BI"},120540:{c:"\\3F5",f:"BI"},120541:{c:"\\3D1",f:"BI"},120542:{c:"\\E009",f:"A"},120543:{c:"\\3D5",f:"BI"},120544:{c:"\\3F1",f:"BI"},120545:{c:"\\3D6",f:"BI"},120546:{c:"A",f:"I"},120547:{c:"B",f:"I"},120548:{c:"\\393",f:"I"},120549:{c:"\\394",f:"I"},120550:{c:"E",f:"I"},120551:{c:"Z",f:"I"},120552:{c:"H",f:"I"},120553:{c:"\\398",f:"I"},120554:{c:"I",f:"I"},120555:{c:"K",f:"I"},120556:{c:"\\39B",f:"I"},120557:{c:"M",f:"I"},120558:{c:"N",f:"I"},120559:{c:"\\39E",f:"I"},120560:{c:"O",f:"I"},120561:{c:"\\3A0",f:"I"},120562:{c:"P",f:"I"},120563:{c:"\\398",f:"I"},120564:{c:"\\3A3",f:"I"},120565:{c:"T",f:"I"},120566:{c:"\\3A5",f:"I"},120567:{c:"\\3A6",f:"I"},120568:{c:"X",f:"I"},120569:{c:"\\3A8",f:"I"},120570:{c:"\\3A9",f:"I"},120571:{c:"\\2207",f:""},120572:{c:"\\3B1",f:"I"},120573:{c:"\\3B2",f:"I"},120574:{c:"\\3B3",f:"I"},120575:{c:"\\3B4",f:"I"},120576:{c:"\\3B5",f:"I"},120577:{c:"\\3B6",f:"I"},120578:{c:"\\3B7",f:"I"},120579:{c:"\\3B8",f:"I"},120580:{c:"\\3B9",f:"I"},120581:{c:"\\3BA",f:"I"},120582:{c:"\\3BB",f:"I"},120583:{c:"\\3BC",f:"I"},120584:{c:"\\3BD",f:"I"},120585:{c:"\\3BE",f:"I"},120586:{c:"\\3BF",f:"I"},120587:{c:"\\3C0",f:"I"},120588:{c:"\\3C1",f:"I"},120589:{c:"\\3C2",f:"I"},120590:{c:"\\3C3",f:"I"},120591:{c:"\\3C4",f:"I"},120592:{c:"\\3C5",f:"I"},120593:{c:"\\3C6",f:"I"},120594:{c:"\\3C7",f:"I"},120595:{c:"\\3C8",f:"I"},120596:{c:"\\3C9",f:"I"},120597:{c:"\\2202",f:""},120598:{c:"\\3F5",f:"I"},120599:{c:"\\3D1",f:"I"},120600:{c:"\\E009",f:"A"},120601:{c:"\\3D5",f:"I"},120602:{c:"\\3F1",f:"I"},120603:{c:"\\3D6",f:"I"},120604:{c:"A",f:"BI"},120605:{c:"B",f:"BI"},120606:{c:"\\393",f:"BI"},120607:{c:"\\394",f:"BI"},120608:{c:"E",f:"BI"},120609:{c:"Z",f:"BI"},120610:{c:"H",f:"BI"},120611:{c:"\\398",f:"BI"},120612:{c:"I",f:"BI"},120613:{c:"K",f:"BI"},120614:{c:"\\39B",f:"BI"},120615:{c:"M",f:"BI"},120616:{c:"N",f:"BI"},120617:{c:"\\39E",f:"BI"},120618:{c:"O",f:"BI"},120619:{c:"\\3A0",f:"BI"},120620:{c:"P",f:"BI"},120621:{c:"\\398",f:"BI"},120622:{c:"\\3A3",f:"BI"},120623:{c:"T",f:"BI"},120624:{c:"\\3A5",f:"BI"},120625:{c:"\\3A6",f:"BI"},120626:{c:"X",f:"BI"},120627:{c:"\\3A8",f:"BI"},120628:{c:"\\3A9",f:"BI"},120629:{c:"\\2207",f:""},120630:{c:"\\3B1",f:"BI"},120631:{c:"\\3B2",f:"BI"},120632:{c:"\\3B3",f:"BI"},120633:{c:"\\3B4",f:"BI"},120634:{c:"\\3B5",f:"BI"},120635:{c:"\\3B6",f:"BI"},120636:{c:"\\3B7",f:"BI"},120637:{c:"\\3B8",f:"BI"},120638:{c:"\\3B9",f:"BI"},120639:{c:"\\3BA",f:"BI"},120640:{c:"\\3BB",f:"BI"},120641:{c:"\\3BC",f:"BI"},120642:{c:"\\3BD",f:"BI"},120643:{c:"\\3BE",f:"BI"},120644:{c:"\\3BF",f:"BI"},120645:{c:"\\3C0",f:"BI"},120646:{c:"\\3C1",f:"BI"},120647:{c:"\\3C2",f:"BI"},120648:{c:"\\3C3",f:"BI"},120649:{c:"\\3C4",f:"BI"},120650:{c:"\\3C5",f:"BI"},120651:{c:"\\3C6",f:"BI"},120652:{c:"\\3C7",f:"BI"},120653:{c:"\\3C8",f:"BI"},120654:{c:"\\3C9",f:"BI"},120655:{c:"\\2202",f:""},120656:{c:"\\3F5",f:"BI"},120657:{c:"\\3D1",f:"BI"},120658:{c:"\\E009",f:"A"},120659:{c:"\\3D5",f:"BI"},120660:{c:"\\3F1",f:"BI"},120661:{c:"\\3D6",f:"BI"},120662:{c:"A",f:"SS-B"},120663:{c:"B",f:"SS-B"},120664:{c:"\\393",f:"SS-B"},120665:{c:"\\394",f:"SS-B"},120666:{c:"E",f:"SS-B"},120667:{c:"Z",f:"SS-B"},120668:{c:"H",f:"SS-B"},120669:{c:"\\398",f:"SS-B"},120670:{c:"I",f:"SS-B"},120671:{c:"K",f:"SS-B"},120672:{c:"\\39B",f:"SS-B"},120673:{c:"M",f:"SS-B"},120674:{c:"N",f:"SS-B"},120675:{c:"\\39E",f:"SS-B"},120676:{c:"O",f:"SS-B"},120677:{c:"\\3A0",f:"SS-B"},120678:{c:"P",f:"SS-B"},120679:{c:"\\398",f:"SS-B"},120680:{c:"\\3A3",f:"SS-B"},120681:{c:"T",f:"SS-B"},120682:{c:"\\3A5",f:"SS-B"},120683:{c:"\\3A6",f:"SS-B"},120684:{c:"X",f:"SS-B"},120685:{c:"\\3A8",f:"SS-B"},120686:{c:"\\3A9",f:"SS-B"},120687:{c:"\\2207",f:""},120688:{c:"\\3B1",f:"BI"},120689:{c:"\\3B2",f:"BI"},120690:{c:"\\3B3",f:"BI"},120691:{c:"\\3B4",f:"BI"},120692:{c:"\\3B5",f:"BI"},120693:{c:"\\3B6",f:"BI"},120694:{c:"\\3B7",f:"BI"},120695:{c:"\\3B8",f:"BI"},120696:{c:"\\3B9",f:"BI"},120697:{c:"\\3BA",f:"BI"},120698:{c:"\\3BB",f:"BI"},120699:{c:"\\3BC",f:"BI"},120700:{c:"\\3BD",f:"BI"},120701:{c:"\\3BE",f:"BI"},120702:{c:"\\3BF",f:"BI"},120703:{c:"\\3C0",f:"BI"},120704:{c:"\\3C1",f:"BI"},120705:{c:"\\3C2",f:"BI"},120706:{c:"\\3C3",f:"BI"},120707:{c:"\\3C4",f:"BI"},120708:{c:"\\3C5",f:"BI"},120709:{c:"\\3C6",f:"BI"},120710:{c:"\\3C7",f:"BI"},120711:{c:"\\3C8",f:"BI"},120712:{c:"\\3C9",f:"BI"},120713:{c:"\\2202",f:""},120714:{c:"\\3F5",f:"BI"},120715:{c:"\\3D1",f:"BI"},120716:{c:"\\E009",f:"A"},120717:{c:"\\3D5",f:"BI"},120718:{c:"\\3F1",f:"BI"},120719:{c:"\\3D6",f:"BI"},120720:{c:"A",f:"SS-I"},120721:{c:"B",f:"SS-I"},120722:{c:"\\393",f:"SS-I"},120723:{c:"\\394",f:"SS-I"},120724:{c:"E",f:"SS-I"},120725:{c:"Z",f:"SS-I"},120726:{c:"H",f:"SS-I"},120727:{c:"\\398",f:"SS-I"},120728:{c:"I",f:"SS-I"},120729:{c:"K",f:"SS-I"},120730:{c:"\\39B",f:"SS-I"},120731:{c:"M",f:"SS-I"},120732:{c:"N",f:"SS-I"},120733:{c:"\\39E",f:"SS-I"},120734:{c:"O",f:"SS-I"},120735:{c:"\\3A0",f:"SS-I"},120736:{c:"P",f:"SS-I"},120737:{c:"\\398",f:"SS-I"},120738:{c:"\\3A3",f:"SS-I"},120739:{c:"T",f:"SS-I"},120740:{c:"\\3A5",f:"SS-I"},120741:{c:"\\3A6",f:"SS-I"},120742:{c:"X",f:"SS-I"},120743:{c:"\\3A8",f:"SS-I"},120744:{c:"\\3A9",f:"SS-I"},120745:{c:"\\2207",f:""},120746:{c:"\\3B1",f:"BI"},120747:{c:"\\3B2",f:"BI"},120748:{c:"\\3B3",f:"BI"},120749:{c:"\\3B4",f:"BI"},120750:{c:"\\3B5",f:"BI"},120751:{c:"\\3B6",f:"BI"},120752:{c:"\\3B7",f:"BI"},120753:{c:"\\3B8",f:"BI"},120754:{c:"\\3B9",f:"BI"},120755:{c:"\\3BA",f:"BI"},120756:{c:"\\3BB",f:"BI"},120757:{c:"\\3BC",f:"BI"},120758:{c:"\\3BD",f:"BI"},120759:{c:"\\3BE",f:"BI"},120760:{c:"\\3BF",f:"BI"},120761:{c:"\\3C0",f:"BI"},120762:{c:"\\3C1",f:"BI"},120763:{c:"\\3C2",f:"BI"},120764:{c:"\\3C3",f:"BI"},120765:{c:"\\3C4",f:"BI"},120766:{c:"\\3C5",f:"BI"},120767:{c:"\\3C6",f:"BI"},120768:{c:"\\3C7",f:"BI"},120769:{c:"\\3C8",f:"BI"},120770:{c:"\\3C9",f:"BI"},120771:{c:"\\2202",f:""},120772:{c:"\\3F5",f:"BI"},120773:{c:"\\3D1",f:"BI"},120774:{c:"\\E009",f:"A"},120775:{c:"\\3D5",f:"BI"},120776:{c:"\\3F1",f:"BI"},120777:{c:"\\3D6",f:"BI"},120778:{c:"F",f:"I"},120779:{c:"\\3DD",f:"A"},120782:{c:"0",f:"B"},120783:{c:"1",f:"B"},120784:{c:"2",f:"B"},120785:{c:"3",f:"B"},120786:{c:"4",f:"B"},120787:{c:"5",f:"B"},120788:{c:"6",f:"B"},120789:{c:"7",f:"B"},120790:{c:"8",f:"B"},120791:{c:"9",f:"B"},120792:{c:"0",f:"B"},120793:{c:"1",f:"B"},120794:{c:"2",f:"B"},120795:{c:"3",f:"B"},120796:{c:"4",f:"B"},120797:{c:"5",f:"B"},120798:{c:"6",f:"B"},120799:{c:"7",f:"B"},120800:{c:"8",f:"B"},120801:{c:"9",f:"B"},120802:{c:"0",f:"SS"},120803:{c:"1",f:"SS"},120804:{c:"2",f:"SS"},120805:{c:"3",f:"SS"},120806:{c:"4",f:"SS"},120807:{c:"5",f:"SS"},120808:{c:"6",f:"SS"},120809:{c:"7",f:"SS"},120810:{c:"8",f:"SS"},120811:{c:"9",f:"SS"},120812:{c:"0",f:"SS-B"},120813:{c:"1",f:"SS-B"},120814:{c:"2",f:"SS-B"},120815:{c:"3",f:"SS-B"},120816:{c:"4",f:"SS-B"},120817:{c:"5",f:"SS-B"},120818:{c:"6",f:"SS-B"},120819:{c:"7",f:"SS-B"},120820:{c:"8",f:"SS-B"},120821:{c:"9",f:"SS-B"},120822:{c:"0",f:"T"},120823:{c:"1",f:"T"},120824:{c:"2",f:"T"},120825:{c:"3",f:"T"},120826:{c:"4",f:"T"},120827:{c:"5",f:"T"},120828:{c:"6",f:"T"},120829:{c:"7",f:"T"},120830:{c:"8",f:"T"},120831:{c:"9",f:"T"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.normal={32:[0,0,.25],33:[.716,0,.278],34:[.694,-.379,.5],35:[.694,.194,.833],36:[.75,.056,.5],37:[.75,.056,.833],38:[.716,.022,.778],39:[.694,-.379,.278],40:[.75,.25,.389],41:[.75,.25,.389],42:[.75,-.32,.5],43:[.583,.082,.778],44:[.121,.194,.278],45:[.252,-.179,.333],46:[.12,0,.278],47:[.75,.25,.5],48:[.666,.022,.5],49:[.666,0,.5],50:[.666,0,.5],51:[.665,.022,.5],52:[.677,0,.5],53:[.666,.022,.5],54:[.666,.022,.5],55:[.676,.022,.5],56:[.666,.022,.5],57:[.666,.022,.5],58:[.43,0,.278],59:[.43,.194,.278],60:[.54,.04,.778],61:[.583,.082,.778],62:[.54,.04,.778],63:[.705,0,.472],64:[.705,.011,.778],65:[.716,0,.75],66:[.683,0,.708],67:[.705,.021,.722],68:[.683,0,.764],69:[.68,0,.681],70:[.68,0,.653],71:[.705,.022,.785],72:[.683,0,.75],73:[.683,0,.361],74:[.683,.022,.514],75:[.683,0,.778],76:[.683,0,.625],77:[.683,0,.917],78:[.683,0,.75],79:[.705,.022,.778],80:[.683,0,.681],81:[.705,.193,.778],82:[.683,.022,.736],83:[.705,.022,.556],84:[.677,0,.722],85:[.683,.022,.75],86:[.683,.022,.75],87:[.683,.022,1.028],88:[.683,0,.75],89:[.683,0,.75],90:[.683,0,.611],91:[.75,.25,.278],92:[.75,.25,.5],93:[.75,.25,.278],94:[.694,-.531,.5],95:[-.025,.062,.5],96:[.699,-.505,.5],97:[.448,.011,.5],98:[.694,.011,.556],99:[.448,.011,.444],100:[.694,.011,.556],101:[.448,.011,.444],102:[.705,0,.306,{ic:.066}],103:[.453,.206,.5],104:[.694,0,.556],105:[.669,0,.278],106:[.669,.205,.306],107:[.694,0,.528],108:[.694,0,.278],109:[.442,0,.833],110:[.442,0,.556],111:[.448,.01,.5],112:[.442,.194,.556],113:[.442,.194,.528],114:[.442,0,.392],115:[.448,.011,.394],116:[.615,.01,.389],117:[.442,.011,.556],118:[.431,.011,.528],119:[.431,.011,.722],120:[.431,0,.528],121:[.431,.204,.528],122:[.431,0,.444],123:[.75,.25,.5],124:[.75,.249,.278],125:[.75,.25,.5],126:[.318,-.215,.5],160:[0,0,.25],163:[.714,.011,.769],165:[.683,0,.75],168:[.669,-.554,.5],172:[.356,-.089,.667],174:[.709,.175,.947],175:[.59,-.544,.5],176:[.715,-.542,.5],177:[.666,0,.778],180:[.699,-.505,.5],183:[.31,-.19,.278],215:[.491,-.009,.778],240:[.749,.021,.556],247:[.537,.036,.778],295:[.695,.013,.54],305:[.442,0,.278,{sk:.0278}],567:[.442,.205,.306,{sk:.0833}],697:[.56,-.043,.275],710:[.694,-.531,.5],711:[.644,-.513,.5],713:[.59,-.544,.5],714:[.699,-.505,.5],715:[.699,-.505,.5],728:[.694,-.515,.5],729:[.669,-.549,.5],730:[.715,-.542,.5],732:[.668,-.565,.5],768:[.699,-.505,0],769:[.699,-.505,0],770:[.694,-.531,0],771:[.668,-.565,0],772:[.59,-.544,0],774:[.694,-.515,0],775:[.669,-.549,0],776:[.669,-.554,0],778:[.715,-.542,0],779:[.701,-.51,0],780:[.644,-.513,0],824:[.716,.215,0],913:[.716,0,.75],914:[.683,0,.708],915:[.68,0,.625],916:[.716,0,.833],917:[.68,0,.681],918:[.683,0,.611],919:[.683,0,.75],920:[.705,.022,.778],921:[.683,0,.361],922:[.683,0,.778],923:[.716,0,.694],924:[.683,0,.917],925:[.683,0,.75],926:[.677,0,.667],927:[.705,.022,.778],928:[.68,0,.75],929:[.683,0,.681],930:[.705,.022,.778],931:[.683,0,.722],932:[.677,0,.722],933:[.705,0,.778],934:[.683,0,.722],935:[.683,0,.75],936:[.683,0,.778],937:[.704,0,.722],978:[.705,0,.778],988:[.68,0,.653],989:[.605,.085,.778],1008:[.434,.006,.667,{ic:.067}],1014:[.44,0,.429],8192:[0,0,.5],8193:[0,0,1],8194:[0,0,.5],8195:[0,0,1],8196:[0,0,.333],8197:[0,0,.25],8198:[0,0,.167],8201:[0,0,.167],8202:[0,0,.1],8203:[0,0,0],8204:[0,0,0],8211:[.285,-.248,.5],8212:[.285,-.248,1],8213:[.285,-.248,1],8214:[.75,.25,.5],8215:[-.025,.062,.5],8216:[.694,-.379,.278],8217:[.694,-.379,.278],8220:[.694,-.379,.5],8221:[.694,-.379,.5],8224:[.705,.216,.444],8225:[.705,.205,.444],8226:[.444,-.055,.5],8230:[.12,0,1.172],8242:[.56,-.043,.275],8243:[.56,0,.55],8244:[.56,0,.825],8245:[.56,-.043,.275],8246:[.56,0,.55],8247:[.56,0,.825],8254:[.59,-.544,.5],8260:[.75,.25,.5],8279:[.56,0,1.1],8289:[0,0,0],8290:[0,0,0],8291:[0,0,0],8292:[0,0,0],8407:[.714,-.516,.5],8450:[.702,.019,.722],8459:[.717,.036,.969,{ic:.272,sk:.333}],8460:[.666,.133,.72],8461:[.683,0,.778],8462:[.694,.011,.576,{sk:-.0278}],8463:[.695,.013,.54],8464:[.717,.314,1.052,{ic:.081,sk:.417}],8465:[.686,.026,.554],8466:[.717,.017,.874,{ic:.161,sk:.306}],8467:[.705,.02,.417,{sk:.111}],8469:[.683,.02,.722],8472:[.453,.216,.636,{sk:.111}],8473:[.683,0,.611],8474:[.701,.181,.778],8475:[.717,.017,.85,{sk:.194}],8476:[.686,.026,.828],8477:[.683,0,.722],8484:[.683,0,.667],8486:[.704,0,.722],8487:[.684,.022,.722],8488:[.729,.139,.602],8492:[.708,.028,.908,{sk:.194}],8493:[.685,.024,.613],8496:[.707,.008,.562,{ic:.156,sk:.139}],8497:[.735,.036,.895,{ic:.095,sk:.222}],8498:[.695,0,.556],8499:[.721,.05,1.08,{ic:.136,sk:.444}],8501:[.694,0,.611],8502:[.763,.021,.667],8503:[.764,.043,.444],8504:[.764,.043,.667],8513:[.705,.023,.639],8592:[.511,.011,1],8593:[.694,.193,.5],8594:[.511,.011,1],8595:[.694,.194,.5],8596:[.511,.011,1],8597:[.772,.272,.5],8598:[.72,.195,1],8599:[.72,.195,1],8600:[.695,.22,1],8601:[.695,.22,1],8602:[.437,-.06,1],8603:[.437,-.06,1],8606:[.417,-.083,1],8608:[.417,-.083,1],8610:[.417,-.083,1.111],8611:[.417,-.083,1.111],8614:[.511,.011,1],8617:[.511,.011,1.126],8618:[.511,.011,1.126],8619:[.575,.041,1],8620:[.575,.041,1],8621:[.417,-.083,1.389],8622:[.437,-.06,1],8624:[.722,0,.5],8625:[.722,0,.5],8630:[.461,0,1],8631:[.46,0,1],8634:[.65,.083,.778],8635:[.65,.083,.778],8636:[.511,-.23,1],8637:[.27,.011,1],8638:[.694,.194,.417],8639:[.694,.194,.417],8640:[.511,-.23,1],8641:[.27,.011,1],8642:[.694,.194,.417],8643:[.694,.194,.417],8644:[.667,0,1],8646:[.667,0,1],8647:[.583,.083,1],8648:[.694,.193,.833],8649:[.583,.083,1],8650:[.694,.194,.833],8651:[.514,.014,1],8652:[.671,.011,1],8653:[.534,.035,1],8654:[.534,.037,1],8655:[.534,.035,1],8656:[.525,.024,1],8657:[.694,.194,.611],8658:[.525,.024,1],8659:[.694,.194,.611],8660:[.526,.025,1],8661:[.772,.272,.611],8666:[.611,.111,1],8667:[.611,.111,1],8669:[.417,-.083,1],8672:[.437,-.064,1.334],8674:[.437,-.064,1.334],8704:[.694,.022,.556],8705:[.846,.021,.5],8706:[.715,.022,.531,{sk:.0833}],8707:[.694,0,.556],8708:[.716,.215,.556],8709:[.772,.078,.5],8710:[.716,0,.833],8711:[.683,.033,.833],8712:[.54,.04,.667],8713:[.716,.215,.667],8715:[.54,.04,.667],8716:[.716,.215,.667],8717:[.44,0,.429],8719:[.75,.25,.944],8720:[.75,.25,.944],8721:[.75,.25,1.056],8722:[.583,.082,.778],8723:[.5,.166,.778],8724:[.766,.093,.778],8725:[.75,.25,.5],8726:[.43,.023,.778],8727:[.465,-.035,.5],8728:[.444,-.055,.5],8729:[.444,-.055,.5],8730:[.8,.2,.833],8733:[.442,.011,.778],8734:[.442,.011,1],8736:[.694,0,.722],8737:[.714,.02,.722],8738:[.551,.051,.722],8739:[.75,.249,.278],8740:[.75,.252,.278],8741:[.75,.25,.5],8742:[.75,.25,.5],8743:[.598,.022,.667],8744:[.598,.022,.667],8745:[.598,.022,.667],8746:[.598,.022,.667],8747:[.716,.216,.417,{ic:.055}],8748:[.805,.306,.819,{ic:.138}],8749:[.805,.306,1.166,{ic:.138}],8750:[.805,.306,.472,{ic:.138}],8756:[.471,.082,.667],8757:[.471,.082,.667],8764:[.367,-.133,.778],8765:[.367,-.133,.778],8768:[.583,.083,.278],8769:[.467,-.032,.778],8770:[.463,-.034,.778],8771:[.464,-.036,.778],8772:[.716,.215,.778],8773:[.589,-.022,.778],8774:[.652,.155,.778],8775:[.652,.155,.778],8776:[.483,-.055,.778],8777:[.716,.215,.778],8778:[.579,.039,.778],8781:[.484,-.016,.778],8782:[.492,-.008,.778],8783:[.492,-.133,.778],8784:[.67,-.133,.778],8785:[.609,.108,.778],8786:[.601,.101,.778],8787:[.601,.102,.778],8790:[.367,-.133,.778],8791:[.721,-.133,.778],8796:[.859,-.133,.778],8800:[.716,.215,.778],8801:[.464,-.036,.778],8802:[.716,.215,.778],8804:[.636,.138,.778],8805:[.636,.138,.778],8806:[.753,.175,.778],8807:[.753,.175,.778],8808:[.752,.286,.778],8809:[.752,.286,.778],8810:[.568,.067,1],8811:[.567,.067,1],8812:[.75,.25,.5],8813:[.716,.215,.778],8814:[.708,.209,.778],8815:[.708,.209,.778],8816:[.801,.303,.778],8817:[.801,.303,.778],8818:[.732,.228,.778],8819:[.732,.228,.778],8820:[.732,.228,.778],8821:[.732,.228,.778],8822:[.681,.253,.778],8823:[.681,.253,.778],8824:[.716,.253,.778],8825:[.716,.253,.778],8826:[.539,.041,.778],8827:[.539,.041,.778],8828:[.58,.153,.778],8829:[.58,.154,.778],8830:[.732,.228,.778],8831:[.732,.228,.778],8832:[.705,.208,.778],8833:[.705,.208,.778],8834:[.54,.04,.778],8835:[.54,.04,.778],8836:[.716,.215,.778],8837:[.716,.215,.778],8838:[.636,.138,.778],8839:[.636,.138,.778],8840:[.801,.303,.778],8841:[.801,.303,.778],8842:[.635,.241,.778],8843:[.635,.241,.778],8846:[.598,.022,.667],8847:[.539,.041,.778],8848:[.539,.041,.778],8849:[.636,.138,.778],8850:[.636,.138,.778],8851:[.598,0,.667],8852:[.598,0,.667],8853:[.583,.083,.778],8854:[.583,.083,.778],8855:[.583,.083,.778],8856:[.583,.083,.778],8857:[.583,.083,.778],8858:[.582,.082,.778],8859:[.582,.082,.778],8861:[.582,.082,.778],8862:[.689,0,.778],8863:[.689,0,.778],8864:[.689,0,.778],8865:[.689,0,.778],8866:[.694,0,.611],8867:[.694,0,.611],8868:[.668,0,.778],8869:[.668,0,.778],8872:[.75,.249,.867],8873:[.694,0,.722],8874:[.694,0,.889],8876:[.695,0,.611],8877:[.695,0,.611],8878:[.695,0,.722],8879:[.695,0,.722],8882:[.539,.041,.778],8883:[.539,.041,.778],8884:[.636,.138,.778],8885:[.636,.138,.778],8888:[.408,-.092,1.111],8890:[.431,.212,.556],8891:[.716,0,.611],8892:[.716,0,.611],8896:[.75,.249,.833],8897:[.75,.249,.833],8898:[.75,.249,.833],8899:[.75,.249,.833],8900:[.488,-.012,.5],8901:[.31,-.19,.278],8902:[.486,-.016,.5],8903:[.545,.044,.778],8904:[.505,.005,.9],8905:[.492,-.008,.778],8906:[.492,-.008,.778],8907:[.694,.022,.778],8908:[.694,.022,.778],8909:[.464,-.036,.778],8910:[.578,.021,.76],8911:[.578,.022,.76],8912:[.54,.04,.778],8913:[.54,.04,.778],8914:[.598,.022,.667],8915:[.598,.022,.667],8916:[.736,.022,.667],8918:[.541,.041,.778],8919:[.541,.041,.778],8920:[.568,.067,1.333],8921:[.568,.067,1.333],8922:[.886,.386,.778],8923:[.886,.386,.778],8926:[.734,0,.778],8927:[.734,0,.778],8928:[.801,.303,.778],8929:[.801,.303,.778],8930:[.716,.215,.778],8931:[.716,.215,.778],8934:[.73,.359,.778],8935:[.73,.359,.778],8936:[.73,.359,.778],8937:[.73,.359,.778],8938:[.706,.208,.778],8939:[.706,.208,.778],8940:[.802,.303,.778],8941:[.801,.303,.778],8942:[1.3,.03,.278],8943:[.31,-.19,1.172],8945:[1.52,-.1,1.282],8965:[.716,0,.611],8966:[.813,.097,.611],8968:[.75,.25,.444],8969:[.75,.25,.444],8970:[.75,.25,.444],8971:[.75,.25,.444],8988:[.694,-.306,.5],8989:[.694,-.306,.5],8990:[.366,.022,.5],8991:[.366,.022,.5],8994:[.388,-.122,1],8995:[.378,-.134,1],9001:[.75,.25,.389],9002:[.75,.25,.389],9136:[.744,.244,.412],9137:[.744,.244,.412],9168:[.602,0,.667],9416:[.709,.175,.902],9484:[.694,-.306,.5],9488:[.694,-.306,.5],9492:[.366,.022,.5],9496:[.366,.022,.5],9585:[.694,.195,.889],9586:[.694,.195,.889],9632:[.689,0,.778],9633:[.689,0,.778],9642:[.689,0,.778],9650:[.575,.02,.722],9651:[.716,0,.889],9652:[.575,.02,.722],9653:[.716,0,.889],9654:[.539,.041,.778],9656:[.539,.041,.778],9657:[.505,.005,.5],9660:[.576,.019,.722],9661:[.5,.215,.889],9662:[.576,.019,.722],9663:[.5,.215,.889],9664:[.539,.041,.778],9666:[.539,.041,.778],9667:[.505,.005,.5],9674:[.716,.132,.667],9711:[.715,.215,1],9723:[.689,0,.778],9724:[.689,0,.778],9733:[.694,.111,.944],9824:[.727,.13,.778],9825:[.716,.033,.778],9826:[.727,.162,.778],9827:[.726,.13,.778],9837:[.75,.022,.389],9838:[.734,.223,.389],9839:[.723,.223,.389],10003:[.706,.034,.833],10016:[.716,.022,.833],10072:[.75,.249,.278],10216:[.75,.25,.389],10217:[.75,.25,.389],10222:[.744,.244,.412],10223:[.744,.244,.412],10229:[.511,.011,1.609],10230:[.511,.011,1.638],10231:[.511,.011,1.859],10232:[.525,.024,1.609],10233:[.525,.024,1.638],10234:[.525,.024,1.858],10236:[.511,.011,1.638],10731:[.716,.132,.667],10744:[.716,.215,.778],10752:[.75,.25,1.111],10753:[.75,.25,1.111],10754:[.75,.25,1.111],10756:[.75,.249,.833],10758:[.75,.249,.833],10764:[.805,.306,1.638,{ic:.138}],10799:[.491,-.009,.778],10815:[.683,0,.75],10846:[.813,.097,.611],10877:[.636,.138,.778],10878:[.636,.138,.778],10885:[.762,.29,.778],10886:[.762,.29,.778],10887:[.635,.241,.778],10888:[.635,.241,.778],10889:[.761,.387,.778],10890:[.761,.387,.778],10891:[1.003,.463,.778],10892:[1.003,.463,.778],10901:[.636,.138,.778],10902:[.636,.138,.778],10927:[.636,.138,.778],10928:[.636,.138,.778],10933:[.752,.286,.778],10934:[.752,.286,.778],10935:[.761,.294,.778],10936:[.761,.294,.778],10937:[.761,.337,.778],10938:[.761,.337,.778],10949:[.753,.215,.778],10950:[.753,.215,.778],10955:[.783,.385,.778],10956:[.783,.385,.778],12296:[.75,.25,.389],12297:[.75,.25,.389],57350:[.43,.023,.222],57351:[.431,.024,.389],57352:[.605,.085,.778],57353:[.434,.006,.667,{ic:.067}],57356:[.752,.284,.778],57357:[.752,.284,.778],57358:[.919,.421,.778],57359:[.801,.303,.778],57360:[.801,.303,.778],57361:[.919,.421,.778],57366:[.828,.33,.778],57367:[.752,.332,.778],57368:[.828,.33,.778],57369:[.752,.333,.778],57370:[.634,.255,.778],57371:[.634,.254,.778],119808:[.698,0,.869],119809:[.686,0,.818],119810:[.697,.011,.831],119811:[.686,0,.882],119812:[.68,0,.756],119813:[.68,0,.724],119814:[.697,.01,.904],119815:[.686,0,.9],119816:[.686,0,.436],119817:[.686,.011,.594],119818:[.686,0,.901],119819:[.686,0,.692],119820:[.686,0,1.092],119821:[.686,0,.9],119822:[.696,.01,.864],119823:[.686,0,.786],119824:[.696,.193,.864],119825:[.686,.011,.862],119826:[.697,.011,.639],119827:[.675,0,.8],119828:[.686,.011,.885],119829:[.686,.007,.869],119830:[.686,.007,1.189],119831:[.686,0,.869],119832:[.686,0,.869],119833:[.686,0,.703],119834:[.453,.006,.559],119835:[.694,.006,.639],119836:[.453,.006,.511],119837:[.694,.006,.639],119838:[.452,.006,.527],119839:[.7,0,.351,{ic:.101}],119840:[.455,.201,.575],119841:[.694,0,.639],119842:[.695,0,.319],119843:[.695,.2,.351],119844:[.694,0,.607],119845:[.694,0,.319],119846:[.45,0,.958],119847:[.45,0,.639],119848:[.452,.005,.575],119849:[.45,.194,.639],119850:[.45,.194,.607],119851:[.45,0,.474],119852:[.453,.006,.454],119853:[.635,.005,.447],119854:[.45,.006,.639],119855:[.444,0,.607],119856:[.444,0,.831],119857:[.444,0,.607],119858:[.444,.2,.607],119859:[.444,0,.511],119860:[.716,0,.75,{sk:.139}],119861:[.683,0,.759,{sk:.0833}],119862:[.705,.022,.715,{sk:.0833}],119863:[.683,0,.828,{sk:.0556}],119864:[.68,0,.738,{sk:.0833}],119865:[.68,0,.643,{ic:.106,sk:.0833}],119866:[.705,.022,.786,{sk:.0833}],119867:[.683,0,.831,{ic:.057,sk:.0556}],119868:[.683,0,.44,{ic:.064,sk:.111}],119869:[.683,.022,.555,{ic:.078,sk:.167}],119870:[.683,0,.849,{sk:.0556}],119871:[.683,0,.681,{sk:.0278}],119872:[.683,0,.97,{ic:.081,sk:.0833}],119873:[.683,0,.803,{ic:.085,sk:.0833}],119874:[.704,.022,.763,{sk:.0833}],119875:[.683,0,.642,{ic:.109,sk:.0833}],119876:[.704,.194,.791,{sk:.0833}],119877:[.683,.021,.759,{sk:.0833}],119878:[.705,.022,.613,{sk:.0833}],119879:[.677,0,.584,{ic:.12,sk:.0833}],119880:[.683,.022,.683,{ic:.084,sk:.0278}],119881:[.683,.022,.583,{ic:.186}],119882:[.683,.022,.944,{ic:.104}],119883:[.683,0,.828,{sk:.0833}],119884:[.683,0,.581,{ic:.182}],119885:[.683,0,.683,{sk:.0833}],119886:[.441,.01,.529],119887:[.694,.011,.429],119888:[.442,.011,.433,{sk:.0556}],119889:[.694,.01,.52,{sk:.167}],119890:[.442,.011,.466,{sk:.0556}],119891:[.705,.205,.49,{ic:.06,sk:.167}],119892:[.442,.205,.477,{sk:.0278}],119893:[.694,.011,.576,{sk:-.0278}],119894:[.661,.011,.345],119895:[.661,.204,.412],119896:[.694,.011,.521],119897:[.694,.011,.298,{sk:.0833}],119898:[.442,.011,.878],119899:[.442,.011,.6],119900:[.441,.011,.485,{sk:.0556}],119901:[.442,.194,.503,{sk:.0833}],119902:[.442,.194,.446,{sk:.0833}],119903:[.442,.011,.451,{sk:.0556}],119904:[.442,.01,.469,{sk:.0556}],119905:[.626,.011,.361,{sk:.0833}],119906:[.442,.011,.572,{sk:.0278}],119907:[.443,.011,.485,{sk:.0278}],119908:[.443,.011,.716,{sk:.0833}],119909:[.442,.011,.572,{sk:.0278}],119910:[.442,.205,.49,{sk:.0556}],119911:[.442,.011,.465,{sk:.0556}],119912:[.711,0,.869,{sk:.16}],119913:[.686,0,.866,{sk:.0958}],119914:[.703,.017,.817,{sk:.0958}],119915:[.686,0,.938,{sk:.0639}],119916:[.68,0,.81,{sk:.0958}],119917:[.68,0,.689,{ic:.12,sk:.0958}],119918:[.703,.016,.887,{sk:.0958}],119919:[.686,0,.982,{sk:.0639}],119920:[.686,0,.511,{ic:.062,sk:.128}],119921:[.686,.017,.631,{ic:.063,sk:.192}],119922:[.686,0,.971,{sk:.0639}],119923:[.686,0,.756,{sk:.0319}],119924:[.686,0,1.142,{ic:.077,sk:.0958}],119925:[.686,0,.95,{ic:.077,sk:.0958}],119926:[.703,.017,.837,{sk:.0958}],119927:[.686,0,.723,{ic:.124,sk:.0958}],119928:[.703,.194,.869,{sk:.0958}],119929:[.686,.017,.872,{sk:.0958}],119930:[.703,.017,.693,{sk:.0958}],119931:[.675,0,.637,{ic:.135,sk:.0958}],119932:[.686,.016,.8,{ic:.077,sk:.0319}],119933:[.686,.016,.678,{ic:.208}],119934:[.686,.017,1.093,{ic:.114}],119935:[.686,0,.947,{sk:.0958}],119936:[.686,0,.675,{ic:.201}],119937:[.686,0,.773,{sk:.0958}],119938:[.452,.008,.633],119939:[.694,.008,.521],119940:[.451,.008,.513,{sk:.0639}],119941:[.694,.008,.61,{sk:.192}],119942:[.452,.008,.554,{sk:.0639}],119943:[.701,.201,.568,{ic:.056,sk:.192}],119944:[.452,.202,.545,{sk:.0319}],119945:[.694,.008,.668,{sk:-.0319}],119946:[.694,.008,.405],119947:[.694,.202,.471],119948:[.694,.008,.604],119949:[.694,.008,.348,{sk:.0958}],119950:[.452,.008,1.032],119951:[.452,.008,.713],119952:[.452,.008,.585,{sk:.0639}],119953:[.452,.194,.601,{sk:.0958}],119954:[.452,.194,.542,{sk:.0958}],119955:[.452,.008,.529,{sk:.0639}],119956:[.451,.008,.531,{sk:.0639}],119957:[.643,.007,.415,{sk:.0958}],119958:[.452,.008,.681,{sk:.0319}],119959:[.453,.008,.567,{sk:.0319}],119960:[.453,.008,.831,{sk:.0958}],119961:[.452,.008,.659,{sk:.0319}],119962:[.452,.202,.59,{sk:.0639}],119963:[.452,.008,.555,{sk:.0639}],119964:[.717,.008,.803,{ic:.213,sk:.389}],119965:[.708,.028,.908,{sk:.194}],119966:[.728,.026,.666,{ic:.153,sk:.278}],119967:[.708,.031,.774,{ic:.081,sk:.111}],119968:[.707,.008,.562,{ic:.156,sk:.139}],119969:[.735,.036,.895,{ic:.095,sk:.222}],119970:[.717,.037,.61,{ic:.128,sk:.25}],119971:[.717,.036,.969,{ic:.272,sk:.333}],119972:[.717,.017,.809,{ic:.137,sk:.333}],119973:[.717,.314,1.052,{ic:.081,sk:.417}],119974:[.717,.037,.914,{ic:.29,sk:.361}],119975:[.717,.017,.874,{ic:.161,sk:.306}],119976:[.721,.05,1.08,{ic:.136,sk:.444}],119977:[.726,.036,.902,{ic:.306,sk:.389}],119978:[.707,.008,.738,{ic:.067,sk:.167}],119979:[.716,.037,1.013,{sk:.222}],119980:[.717,.017,.883,{sk:.278}],119981:[.717,.017,.85,{sk:.194}],119982:[.708,.036,.868,{ic:.148,sk:.333}],119983:[.735,.037,.747,{ic:.249,sk:.222}],119984:[.717,.017,.8,{ic:.16,sk:.25}],119985:[.717,.017,.622,{ic:.228,sk:.222}],119986:[.717,.017,.805,{ic:.221,sk:.25}],119987:[.717,.017,.944,{ic:.187,sk:.278}],119988:[.716,.017,.71,{ic:.249,sk:.194}],119989:[.717,.016,.821,{ic:.211,sk:.306}],119990:[.441,.01,.529],119991:[.694,.011,.429],119992:[.442,.011,.433,{sk:.0556}],119993:[.694,.01,.52,{sk:.167}],119994:[.442,.011,.466,{sk:.0556}],119995:[.705,.205,.49,{ic:.06,sk:.167}],119996:[.442,.205,.477,{sk:.0278}],119997:[.694,.011,.576,{sk:-.0278}],119998:[.661,.011,.345],119999:[.661,.204,.412],12e4:[.694,.011,.521],120001:[.694,.011,.298,{sk:.0833}],120002:[.442,.011,.878],120003:[.442,.011,.6],120004:[.441,.011,.485,{sk:.0556}],120005:[.442,.194,.503,{sk:.0833}],120006:[.442,.194,.446,{sk:.0833}],120007:[.442,.011,.451,{sk:.0556}],120008:[.442,.01,.469,{sk:.0556}],120009:[.626,.011,.361,{sk:.0833}],120010:[.442,.011,.572,{sk:.0278}],120011:[.443,.011,.485,{sk:.0278}],120012:[.443,.011,.716,{sk:.0833}],120013:[.442,.011,.572,{sk:.0278}],120014:[.442,.205,.49,{sk:.0556}],120015:[.442,.011,.465,{sk:.0556}],120016:[.717,.008,.803,{ic:.213,sk:.389}],120017:[.708,.028,.908,{sk:.194}],120018:[.728,.026,.666,{ic:.153,sk:.278}],120019:[.708,.031,.774,{ic:.081,sk:.111}],120020:[.707,.008,.562,{ic:.156,sk:.139}],120021:[.735,.036,.895,{ic:.095,sk:.222}],120022:[.717,.037,.61,{ic:.128,sk:.25}],120023:[.717,.036,.969,{ic:.272,sk:.333}],120024:[.717,.017,.809,{ic:.137,sk:.333}],120025:[.717,.314,1.052,{ic:.081,sk:.417}],120026:[.717,.037,.914,{ic:.29,sk:.361}],120027:[.717,.017,.874,{ic:.161,sk:.306}],120028:[.721,.05,1.08,{ic:.136,sk:.444}],120029:[.726,.036,.902,{ic:.306,sk:.389}],120030:[.707,.008,.738,{ic:.067,sk:.167}],120031:[.716,.037,1.013,{sk:.222}],120032:[.717,.017,.883,{sk:.278}],120033:[.717,.017,.85,{sk:.194}],120034:[.708,.036,.868,{ic:.148,sk:.333}],120035:[.735,.037,.747,{ic:.249,sk:.222}],120036:[.717,.017,.8,{ic:.16,sk:.25}],120037:[.717,.017,.622,{ic:.228,sk:.222}],120038:[.717,.017,.805,{ic:.221,sk:.25}],120039:[.717,.017,.944,{ic:.187,sk:.278}],120040:[.716,.017,.71,{ic:.249,sk:.194}],120041:[.717,.016,.821,{ic:.211,sk:.306}],120042:[.452,.008,.633],120043:[.694,.008,.521],120044:[.451,.008,.513,{sk:.0639}],120045:[.694,.008,.61,{sk:.192}],120046:[.452,.008,.554,{sk:.0639}],120047:[.701,.201,.568,{ic:.056,sk:.192}],120048:[.452,.202,.545,{sk:.0319}],120049:[.694,.008,.668,{sk:-.0319}],120050:[.694,.008,.405],120051:[.694,.202,.471],120052:[.694,.008,.604],120053:[.694,.008,.348,{sk:.0958}],120054:[.452,.008,1.032],120055:[.452,.008,.713],120056:[.452,.008,.585,{sk:.0639}],120057:[.452,.194,.601,{sk:.0958}],120058:[.452,.194,.542,{sk:.0958}],120059:[.452,.008,.529,{sk:.0639}],120060:[.451,.008,.531,{sk:.0639}],120061:[.643,.007,.415,{sk:.0958}],120062:[.452,.008,.681,{sk:.0319}],120063:[.453,.008,.567,{sk:.0319}],120064:[.453,.008,.831,{sk:.0958}],120065:[.452,.008,.659,{sk:.0319}],120066:[.452,.202,.59,{sk:.0639}],120067:[.452,.008,.555,{sk:.0639}],120068:[.696,.026,.718],120069:[.691,.027,.884],120070:[.685,.024,.613],120071:[.685,.027,.832],120072:[.685,.024,.663],120073:[.686,.153,.611],120074:[.69,.026,.785],120075:[.666,.133,.72],120076:[.686,.026,.554],120077:[.686,.139,.552],120078:[.68,.027,.668],120079:[.686,.026,.666],120080:[.692,.027,1.05],120081:[.686,.025,.832],120082:[.729,.027,.827],120083:[.692,.218,.828],120084:[.729,.069,.827],120085:[.686,.026,.828],120086:[.692,.027,.829],120087:[.701,.027,.669],120088:[.697,.027,.646],120089:[.686,.026,.831],120090:[.686,.027,1.046],120091:[.688,.027,.719],120092:[.686,.218,.833],120093:[.729,.139,.602],120094:[.47,.035,.5],120095:[.685,.031,.513],120096:[.466,.029,.389],120097:[.609,.033,.499],120098:[.467,.03,.401],120099:[.681,.221,.326],120100:[.47,.209,.504],120101:[.688,.205,.521],120102:[.673,.02,.279],120103:[.672,.208,.281],120104:[.689,.025,.389],120105:[.685,.02,.28],120106:[.475,.026,.767],120107:[.475,.022,.527],120108:[.48,.028,.489],120109:[.541,.212,.5],120110:[.479,.219,.489],120111:[.474,.021,.389],120112:[.478,.029,.443],120113:[.64,.02,.333],120114:[.474,.023,.517],120115:[.53,.028,.512],120116:[.532,.028,.774],120117:[.472,.188,.389],120118:[.528,.218,.499],120119:[.471,.214,.391],120120:[.701,0,.722],120121:[.683,0,.667],120122:[.702,.019,.722],120123:[.683,0,.722],120124:[.683,0,.667],120125:[.683,0,.611],120126:[.702,.019,.778],120127:[.683,0,.778],120128:[.683,0,.389],120129:[.683,.077,.5],120130:[.683,0,.778],120131:[.683,0,.667],120132:[.683,0,.944],120133:[.683,.02,.722],120134:[.701,.019,.778],120135:[.683,0,.611],120136:[.701,.181,.778],120137:[.683,0,.722],120138:[.702,.012,.556],120139:[.683,0,.667],120140:[.683,.019,.722],120141:[.683,.02,.722],120142:[.683,.019,1],120143:[.683,0,.722],120144:[.683,0,.722],120145:[.683,0,.667],120146:[.453,.006,.559],120147:[.694,.006,.639],120148:[.453,.006,.511],120149:[.694,.006,.639],120150:[.452,.006,.527],120151:[.7,0,.351,{ic:.101}],120152:[.455,.201,.575],120153:[.694,0,.639],120154:[.695,0,.319],120155:[.695,.2,.351],120156:[.683,0,.556],120157:[.694,0,.319],120158:[.45,0,.958],120159:[.45,0,.639],120160:[.452,.005,.575],120161:[.45,.194,.639],120162:[.45,.194,.607],120163:[.45,0,.474],120164:[.453,.006,.454],120165:[.635,.005,.447],120166:[.45,.006,.639],120167:[.444,0,.607],120168:[.444,0,.831],120169:[.444,0,.607],120170:[.444,.2,.607],120171:[.444,0,.511],120172:[.686,.031,.847],120173:[.684,.031,1.044],120174:[.676,.032,.723],120175:[.683,.029,.982],120176:[.686,.029,.783],120177:[.684,.146,.722],120178:[.687,.029,.927],120179:[.683,.126,.851],120180:[.681,.025,.655],120181:[.68,.141,.652],120182:[.681,.026,.789],120183:[.683,.028,.786],120184:[.683,.032,1.239],120185:[.679,.03,.983],120186:[.726,.03,.976],120187:[.688,.223,.977],120188:[.726,.083,.976],120189:[.688,.028,.978],120190:[.685,.031,.978],120191:[.686,.03,.79],120192:[.688,.039,.851],120193:[.685,.029,.982],120194:[.683,.03,1.235],120195:[.681,.035,.849],120196:[.688,.214,.984],120197:[.677,.148,.711],120198:[.472,.032,.603],120199:[.69,.032,.59],120200:[.473,.026,.464],120201:[.632,.028,.589],120202:[.471,.027,.472],120203:[.687,.222,.388],120204:[.472,.208,.595],120205:[.687,.207,.615],120206:[.686,.025,.331],120207:[.682,.203,.332],120208:[.682,.025,.464],120209:[.681,.024,.337],120210:[.476,.031,.921],120211:[.473,.028,.654],120212:[.482,.034,.609],120213:[.557,.207,.604],120214:[.485,.211,.596],120215:[.472,.026,.46],120216:[.479,.034,.523],120217:[.648,.027,.393],120218:[.472,.032,.589],120219:[.546,.027,.604],120220:[.549,.032,.918],120221:[.471,.188,.459],120222:[.557,.221,.589],120223:[.471,.214,.461],120224:[.694,0,.667],120225:[.694,0,.667],120226:[.705,.011,.639],120227:[.694,0,.722],120228:[.691,0,.597],120229:[.691,0,.569],120230:[.704,.011,.667],120231:[.694,0,.708],120232:[.694,0,.278],120233:[.694,.022,.472],120234:[.694,0,.694],120235:[.694,0,.542],120236:[.694,0,.875],120237:[.694,0,.708],120238:[.715,.022,.736],120239:[.694,0,.639],120240:[.715,.125,.736],120241:[.694,0,.646],120242:[.716,.022,.556],120243:[.688,0,.681],120244:[.694,.022,.688],120245:[.694,0,.667],120246:[.694,0,.944],120247:[.694,0,.667],120248:[.694,0,.667],120249:[.694,0,.611],120250:[.46,.01,.481],120251:[.694,.011,.517],120252:[.46,.01,.444],120253:[.694,.01,.517],120254:[.461,.01,.444],120255:[.705,0,.306],120256:[.455,.206,.5],120257:[.694,0,.517],120258:[.68,0,.239],120259:[.68,.205,.267],120260:[.694,0,.489],120261:[.694,0,.239],120262:[.455,0,.794],120263:[.455,0,.517],120264:[.46,.01,.5],120265:[.455,.194,.517],120266:[.455,.194,.517],120267:[.455,0,.342],120268:[.46,.01,.383],120269:[.571,.01,.361],120270:[.444,.01,.517],120271:[.444,0,.461],120272:[.444,0,.683],120273:[.444,0,.461],120274:[.444,.204,.461],120275:[.444,0,.435],120276:[.694,0,.733],120277:[.694,0,.733],120278:[.704,.011,.703],120279:[.694,0,.794],120280:[.691,0,.642],120281:[.691,0,.611],120282:[.705,.011,.733],120283:[.694,0,.794],120284:[.694,0,.331],120285:[.694,.022,.519],120286:[.694,0,.764],120287:[.694,0,.581],120288:[.694,0,.978],120289:[.694,0,.794],120290:[.716,.022,.794],120291:[.694,0,.703],120292:[.716,.106,.794],120293:[.694,0,.703],120294:[.716,.022,.611],120295:[.688,0,.733],120296:[.694,.022,.764],120297:[.694,0,.733],120298:[.694,0,1.039],120299:[.694,0,.733],120300:[.694,0,.733],120301:[.694,0,.672],120302:[.475,.011,.525],120303:[.694,.01,.561],120304:[.475,.011,.489],120305:[.694,.011,.561],120306:[.474,.01,.511],120307:[.705,0,.336],120308:[.469,.206,.55],120309:[.694,0,.561],120310:[.695,0,.256],120311:[.695,.205,.286],120312:[.694,0,.531],120313:[.694,0,.256],120314:[.469,0,.867],120315:[.468,0,.561],120316:[.474,.011,.55],120317:[.469,.194,.561],120318:[.469,.194,.561],120319:[.469,0,.372],120320:[.474,.01,.422],120321:[.589,.01,.404],120322:[.458,.011,.561],120323:[.458,0,.5],120324:[.458,0,.744],120325:[.458,0,.5],120326:[.458,.205,.5],120327:[.458,0,.476],120328:[.694,0,.667],120329:[.694,0,.667],120330:[.705,.01,.639,{ic:.08}],120331:[.694,0,.722],120332:[.691,0,.597,{ic:.091}],120333:[.691,0,.569,{ic:.104}],120334:[.705,.011,.667,{ic:.063}],120335:[.694,0,.708,{ic:.06}],120336:[.694,0,.278,{ic:.06}],120337:[.694,.022,.472,{ic:.063}],120338:[.694,0,.694,{ic:.091}],120339:[.694,0,.542],120340:[.694,0,.875,{ic:.054}],120341:[.694,0,.708,{ic:.058}],120342:[.716,.022,.736],120343:[.694,0,.639,{ic:.051}],120344:[.716,.125,.736],120345:[.694,0,.646,{ic:.052}],120346:[.716,.022,.556,{ic:.053}],120347:[.688,0,.681,{ic:.109}],120348:[.694,.022,.688,{ic:.059}],120349:[.694,0,.667,{ic:.132}],120350:[.694,0,.944,{ic:.132}],120351:[.694,0,.667,{ic:.091}],120352:[.694,0,.667,{ic:.143}],120353:[.694,0,.611,{ic:.091}],120354:[.461,.01,.481],120355:[.694,.011,.517],120356:[.46,.011,.444,{ic:.055}],120357:[.694,.01,.517,{ic:.071}],120358:[.46,.011,.444],120359:[.705,0,.306,{ic:.188}],120360:[.455,.206,.5,{ic:.068}],120361:[.694,0,.517],120362:[.68,0,.239,{ic:.076}],120363:[.68,.204,.267,{ic:.069}],120364:[.694,0,.489,{ic:.054}],120365:[.694,0,.239,{ic:.072}],120366:[.455,0,.794],120367:[.454,0,.517],120368:[.461,.011,.5],120369:[.455,.194,.517],120370:[.455,.194,.517],120371:[.455,0,.342,{ic:.082}],120372:[.461,.011,.383,{ic:.053}],120373:[.571,.011,.361],120374:[.444,.01,.517],120375:[.444,0,.461,{ic:.079}],120376:[.444,0,.683,{ic:.079}],120377:[.444,0,.461,{ic:.076}],120378:[.444,.205,.461,{ic:.079}],120379:[.444,0,.435,{ic:.059}],120380:[.694,0,.667],120381:[.694,0,.667],120382:[.705,.01,.639,{ic:.08}],120383:[.694,0,.722],120384:[.691,0,.597,{ic:.091}],120385:[.691,0,.569,{ic:.104}],120386:[.705,.011,.667,{ic:.063}],120387:[.694,0,.708,{ic:.06}],120388:[.694,0,.278,{ic:.06}],120389:[.694,.022,.472,{ic:.063}],120390:[.694,0,.694,{ic:.091}],120391:[.694,0,.542],120392:[.694,0,.875,{ic:.054}],120393:[.694,0,.708,{ic:.058}],120394:[.716,.022,.736],120395:[.694,0,.639,{ic:.051}],120396:[.716,.125,.736],120397:[.694,0,.646,{ic:.052}],120398:[.716,.022,.556,{ic:.053}],120399:[.688,0,.681,{ic:.109}],120400:[.694,.022,.688,{ic:.059}],120401:[.694,0,.667,{ic:.132}],120402:[.694,0,.944,{ic:.132}],120403:[.694,0,.667,{ic:.091}],120404:[.694,0,.667,{ic:.143}],120405:[.694,0,.611,{ic:.091}],120406:[.461,.01,.481],120407:[.694,.011,.517],120408:[.46,.011,.444,{ic:.055}],120409:[.694,.01,.517,{ic:.071}],120410:[.46,.011,.444],120411:[.705,0,.306,{ic:.188}],120412:[.455,.206,.5,{ic:.068}],120413:[.694,0,.517],120414:[.68,0,.239,{ic:.076}],120415:[.68,.204,.267,{ic:.069}],120416:[.694,0,.489,{ic:.054}],120417:[.694,0,.239,{ic:.072}],120418:[.455,0,.794],120419:[.454,0,.517],120420:[.461,.011,.5],120421:[.455,.194,.517],120422:[.455,.194,.517],120423:[.455,0,.342,{ic:.082}],120424:[.461,.011,.383,{ic:.053}],120425:[.571,.011,.361],120426:[.444,.01,.517],120427:[.444,0,.461,{ic:.079}],120428:[.444,0,.683,{ic:.079}],120429:[.444,0,.461,{ic:.076}],120430:[.444,.205,.461,{ic:.079}],120431:[.444,0,.435,{ic:.059}],120432:[.623,0,.525],120433:[.611,0,.525],120434:[.622,.011,.525],120435:[.611,0,.525],120436:[.611,0,.525],120437:[.611,0,.525],120438:[.622,.011,.525],120439:[.611,0,.525],120440:[.611,0,.525],120441:[.611,.011,.525],120442:[.611,0,.525],120443:[.611,0,.525],120444:[.611,0,.525],120445:[.611,0,.525],120446:[.621,.01,.525],120447:[.611,0,.525],120448:[.621,.138,.525],120449:[.611,.011,.525],120450:[.622,.011,.525],120451:[.611,0,.525],120452:[.611,.011,.525],120453:[.611,.007,.525],120454:[.611,.007,.525],120455:[.611,0,.525],120456:[.611,0,.525],120457:[.611,0,.525],120458:[.439,.006,.525],120459:[.611,.006,.525],120460:[.44,.006,.525],120461:[.611,.006,.525],120462:[.44,.006,.525],120463:[.617,0,.525],120464:[.442,.229,.525],120465:[.611,0,.525],120466:[.612,0,.525],120467:[.612,.228,.525],120468:[.611,0,.525],120469:[.611,0,.525],120470:[.436,0,.525],120471:[.436,0,.525],120472:[.44,.006,.525],120473:[.437,.221,.525],120474:[.437,.221,.525],120475:[.437,0,.525],120476:[.44,.006,.525],120477:[.554,.006,.525],120478:[.431,.005,.525],120479:[.431,0,.525],120480:[.431,0,.525],120481:[.431,0,.525],120482:[.431,.228,.525],120483:[.431,0,.525],120484:[.441,.01,.307],120485:[.442,.204,.332],120488:[.698,0,.869],120489:[.686,0,.818],120490:[.68,0,.692],120491:[.698,0,.958],120492:[.68,0,.756],120493:[.686,0,.703],120494:[.686,0,.9],120495:[.696,.01,.894],120496:[.686,0,.436],120497:[.686,0,.901],120498:[.698,0,.806],120499:[.686,0,1.092],120500:[.686,0,.9],120501:[.675,0,.767],120502:[.696,.01,.864],120503:[.68,0,.9],120504:[.686,0,.786],120505:[.696,.01,.894],120506:[.686,0,.831],120507:[.675,0,.8],120508:[.697,0,.894],120509:[.686,0,.831],120510:[.686,0,.869],120511:[.686,0,.894],120512:[.696,0,.831],120513:[.686,.024,.958],120514:[.452,.008,.761,{sk:.0319}],120515:[.701,.194,.66,{sk:.0958}],120516:[.451,.211,.59],120517:[.725,.008,.522,{sk:.0639}],120518:[.461,.017,.529,{sk:.0958}],120519:[.711,.202,.508,{sk:.0958}],120520:[.452,.211,.6,{sk:.0639}],120521:[.702,.008,.562,{sk:.0958}],120522:[.452,.008,.412,{sk:.0639}],120523:[.452,.008,.668],120524:[.694,.013,.671],120525:[.452,.211,.708,{sk:.0319}],120526:[.452,0,.577,{sk:.0319}],120527:[.711,.201,.508,{sk:.128}],120528:[.452,.008,.585,{sk:.0639}],120529:[.444,.008,.682],120530:[.451,.211,.612,{sk:.0958}],120531:[.451,.105,.424,{sk:.0958}],120532:[.444,.008,.686],120533:[.444,.013,.521,{ic:.089,sk:.0319}],120534:[.453,.008,.631,{sk:.0319}],120535:[.452,.216,.747,{sk:.0958}],120536:[.452,.201,.718,{sk:.0639}],120537:[.694,.202,.758,{sk:.128}],120538:[.453,.008,.718],120539:[.71,.017,.628,{sk:.0958}],120540:[.444,.007,.483,{sk:.0639}],120541:[.701,.008,.692,{sk:.0958}],120542:[.434,.006,.667,{ic:.067}],120543:[.694,.202,.712,{sk:.0958}],120544:[.451,.194,.612,{sk:.0958}],120545:[.444,.008,.975],120546:[.716,0,.75,{sk:.139}],120547:[.683,0,.759,{sk:.0833}],120548:[.68,0,.615,{ic:.106,sk:.0833}],120549:[.716,0,.833,{sk:.167}],120550:[.68,0,.738,{sk:.0833}],120551:[.683,0,.683,{sk:.0833}],120552:[.683,0,.831,{ic:.057,sk:.0556}],120553:[.704,.022,.763,{sk:.0833}],120554:[.683,0,.44,{ic:.064,sk:.111}],120555:[.683,0,.849,{sk:.0556}],120556:[.716,0,.694,{sk:.167}],120557:[.683,0,.97,{ic:.081,sk:.0833}],120558:[.683,0,.803,{ic:.085,sk:.0833}],120559:[.677,0,.742,{sk:.0833}],120560:[.704,.022,.763,{sk:.0833}],120561:[.68,0,.831,{ic:.056,sk:.0556}],120562:[.683,0,.642,{ic:.109,sk:.0833}],120563:[.704,.022,.763,{sk:.0833}],120564:[.683,0,.78,{sk:.0833}],120565:[.677,0,.584,{ic:.12,sk:.0833}],120566:[.705,0,.583,{ic:.117,sk:.0556}],120567:[.683,0,.667,{sk:.0833}],120568:[.683,0,.828,{sk:.0833}],120569:[.683,0,.612,{ic:.08,sk:.0556}],120570:[.704,0,.772,{sk:.0833}],120571:[.683,.033,.833],120572:[.442,.011,.64,{sk:.0278}],120573:[.705,.194,.566,{sk:.0833}],120574:[.441,.216,.518],120575:[.717,.01,.444,{sk:.0556}],120576:[.452,.022,.466,{sk:.0833}],120577:[.704,.204,.438,{sk:.0833}],120578:[.442,.216,.497,{sk:.0556}],120579:[.705,.01,.469,{sk:.0833}],120580:[.442,.01,.354,{sk:.0556}],120581:[.442,.011,.576],120582:[.694,.012,.583],120583:[.442,.216,.603,{sk:.0278}],120584:[.442,0,.494,{sk:.0278}],120585:[.704,.205,.438,{sk:.111}],120586:[.441,.011,.485,{sk:.0556}],120587:[.431,.011,.57],120588:[.442,.216,.517,{sk:.0833}],120589:[.442,.107,.363,{sk:.0833}],120590:[.431,.011,.571],120591:[.431,.013,.437,{ic:.08,sk:.0278}],120592:[.443,.01,.54,{sk:.0278}],120593:[.442,.218,.654,{sk:.0833}],120594:[.442,.204,.626,{sk:.0556}],120595:[.694,.205,.651,{sk:.111}],120596:[.443,.011,.622],120597:[.715,.022,.531,{sk:.0833}],120598:[.431,.011,.406,{sk:.0556}],120599:[.705,.011,.591,{sk:.0833}],120600:[.434,.006,.667,{ic:.067}],120601:[.694,.205,.596,{sk:.0833}],120602:[.442,.194,.517,{sk:.0833}],120603:[.431,.01,.828],120604:[.711,0,.869,{sk:.16}],120605:[.686,0,.866,{sk:.0958}],120606:[.68,0,.657,{ic:.12,sk:.0958}],120607:[.711,0,.958,{sk:.192}],120608:[.68,0,.81,{sk:.0958}],120609:[.686,0,.773,{sk:.0958}],120610:[.686,0,.982,{sk:.0639}],120611:[.702,.017,.867,{sk:.0958}],120612:[.686,0,.511,{ic:.062,sk:.128}],120613:[.686,0,.971,{sk:.0639}],120614:[.711,0,.806,{sk:.192}],120615:[.686,0,1.142,{ic:.077,sk:.0958}],120616:[.686,0,.95,{ic:.077,sk:.0958}],120617:[.675,0,.841,{sk:.0958}],120618:[.703,.017,.837,{sk:.0958}],120619:[.68,0,.982,{sk:.0639}],120620:[.686,0,.723,{ic:.124,sk:.0958}],120621:[.702,.017,.867,{sk:.0958}],120622:[.686,0,.885,{sk:.0958}],120623:[.675,0,.637,{ic:.135,sk:.0958}],120624:[.703,0,.671,{ic:.131,sk:.0639}],120625:[.686,0,.767,{sk:.0958}],120626:[.686,0,.947,{sk:.0958}],120627:[.686,0,.714,{ic:.076,sk:.0639}],120628:[.703,0,.879,{sk:.0958}],120629:[.683,.033,.833],120630:[.452,.008,.761,{sk:.0319}],120631:[.701,.194,.66,{sk:.0958}],120632:[.451,.211,.59],120633:[.725,.008,.522,{sk:.0639}],120634:[.461,.017,.529,{sk:.0958}],120635:[.711,.202,.508,{sk:.0958}],120636:[.452,.211,.6,{sk:.0639}],120637:[.702,.008,.562,{sk:.0958}],120638:[.452,.008,.412,{sk:.0639}],120639:[.452,.008,.668],120640:[.694,.013,.671],120641:[.452,.211,.708,{sk:.0319}],120642:[.452,0,.577,{sk:.0319}],120643:[.711,.201,.508,{sk:.128}],120644:[.452,.008,.585,{sk:.0639}],120645:[.444,.008,.682],120646:[.451,.211,.612,{sk:.0958}],120647:[.451,.105,.424,{sk:.0958}],120648:[.444,.008,.686],120649:[.444,.013,.521,{ic:.089,sk:.0319}],120650:[.453,.008,.631,{sk:.0319}],120651:[.452,.216,.747,{sk:.0958}],120652:[.452,.201,.718,{sk:.0639}],120653:[.694,.202,.758,{sk:.128}],120654:[.453,.008,.718],120655:[.715,.022,.531,{sk:.0833}],120656:[.444,.007,.483,{sk:.0639}],120657:[.701,.008,.692,{sk:.0958}],120658:[.434,.006,.667,{ic:.067}],120659:[.694,.202,.712,{sk:.0958}],120660:[.451,.194,.612,{sk:.0958}],120661:[.444,.008,.975],120662:[.694,0,.733],120663:[.694,0,.733],120664:[.691,0,.581],120665:[.694,0,.917],120666:[.691,0,.642],120667:[.694,0,.672],120668:[.694,0,.794],120669:[.716,.022,.856],120670:[.694,0,.331],120671:[.694,0,.764],120672:[.694,0,.672],120673:[.694,0,.978],120674:[.694,0,.794],120675:[.688,0,.733],120676:[.716,.022,.794],120677:[.691,0,.794],120678:[.694,0,.703],120679:[.716,.022,.856],120680:[.694,0,.794],120681:[.688,0,.733],120682:[.715,0,.856],120683:[.694,0,.794],120684:[.694,0,.733],120685:[.694,0,.856],120686:[.716,0,.794],120687:[.683,.033,.833],120688:[.452,.008,.761,{sk:.0319}],120689:[.701,.194,.66,{sk:.0958}],120690:[.451,.211,.59],120691:[.725,.008,.522,{sk:.0639}],120692:[.461,.017,.529,{sk:.0958}],120693:[.711,.202,.508,{sk:.0958}],120694:[.452,.211,.6,{sk:.0639}],120695:[.702,.008,.562,{sk:.0958}],120696:[.452,.008,.412,{sk:.0639}],120697:[.452,.008,.668],120698:[.694,.013,.671],120699:[.452,.211,.708,{sk:.0319}],120700:[.452,0,.577,{sk:.0319}],120701:[.711,.201,.508,{sk:.128}],120702:[.452,.008,.585,{sk:.0639}],120703:[.444,.008,.682],120704:[.451,.211,.612,{sk:.0958}],120705:[.451,.105,.424,{sk:.0958}],120706:[.444,.008,.686],120707:[.444,.013,.521,{ic:.089,sk:.0319}],120708:[.453,.008,.631,{sk:.0319}],120709:[.452,.216,.747,{sk:.0958}],120710:[.452,.201,.718,{sk:.0639}],120711:[.694,.202,.758,{sk:.128}],120712:[.453,.008,.718],120713:[.715,.022,.531,{sk:.0833}],120714:[.444,.007,.483,{sk:.0639}],120715:[.701,.008,.692,{sk:.0958}],120716:[.434,.006,.667,{ic:.067}],120717:[.694,.202,.712,{sk:.0958}],120718:[.451,.194,.612,{sk:.0958}],120719:[.444,.008,.975],120720:[.694,0,.667],120721:[.694,0,.667],120722:[.691,0,.542,{ic:.104}],120723:[.694,0,.833],120724:[.691,0,.597,{ic:.091}],120725:[.694,0,.611,{ic:.091}],120726:[.694,0,.708,{ic:.06}],120727:[.715,.022,.778],120728:[.694,0,.278,{ic:.06}],120729:[.694,0,.694,{ic:.091}],120730:[.694,0,.611],120731:[.694,0,.875,{ic:.054}],120732:[.694,0,.708,{ic:.058}],120733:[.688,0,.667,{ic:.098}],120734:[.716,.022,.736],120735:[.691,0,.708,{ic:.06}],120736:[.694,0,.639,{ic:.051}],120737:[.715,.022,.778],120738:[.694,0,.722,{ic:.091}],120739:[.688,0,.681,{ic:.109}],120740:[.716,0,.778,{ic:.065}],120741:[.694,0,.722],120742:[.694,0,.667,{ic:.091}],120743:[.694,0,.778,{ic:.076}],120744:[.716,0,.722],120745:[.683,.033,.833],120746:[.452,.008,.761,{sk:.0319}],120747:[.701,.194,.66,{sk:.0958}],120748:[.451,.211,.59],120749:[.725,.008,.522,{sk:.0639}],120750:[.461,.017,.529,{sk:.0958}],120751:[.711,.202,.508,{sk:.0958}],120752:[.452,.211,.6,{sk:.0639}],120753:[.702,.008,.562,{sk:.0958}],120754:[.452,.008,.412,{sk:.0639}],120755:[.452,.008,.668],120756:[.694,.013,.671],120757:[.452,.211,.708,{sk:.0319}],120758:[.452,0,.577,{sk:.0319}],120759:[.711,.201,.508,{sk:.128}],120760:[.452,.008,.585,{sk:.0639}],120761:[.444,.008,.682],120762:[.451,.211,.612,{sk:.0958}],120763:[.451,.105,.424,{sk:.0958}],120764:[.444,.008,.686],120765:[.444,.013,.521,{ic:.089,sk:.0319}],120766:[.453,.008,.631,{sk:.0319}],120767:[.452,.216,.747,{sk:.0958}],120768:[.452,.201,.718,{sk:.0639}],120769:[.694,.202,.758,{sk:.128}],120770:[.453,.008,.718],120771:[.715,.022,.531,{sk:.0833}],120772:[.444,.007,.483,{sk:.0639}],120773:[.701,.008,.692,{sk:.0958}],120774:[.434,.006,.667,{ic:.067}],120775:[.694,.202,.712,{sk:.0958}],120776:[.451,.194,.612,{sk:.0958}],120777:[.444,.008,.975],120778:[.68,0,.643,{ic:.106,sk:.0833}],120779:[.605,.085,.778],120782:[.654,.01,.575],120783:[.655,0,.575],120784:[.654,0,.575],120785:[.655,.011,.575],120786:[.656,0,.575],120787:[.655,.011,.575],120788:[.655,.011,.575],120789:[.676,.011,.575],120790:[.654,.011,.575],120791:[.654,.011,.575],120792:[.654,.01,.575],120793:[.655,0,.575],120794:[.654,0,.575],120795:[.655,.011,.575],120796:[.656,0,.575],120797:[.655,.011,.575],120798:[.655,.011,.575],120799:[.676,.011,.575],120800:[.654,.011,.575],120801:[.654,.011,.575],120802:[.678,.022,.5],120803:[.678,0,.5],120804:[.677,0,.5],120805:[.678,.022,.5],120806:[.656,0,.5],120807:[.656,.021,.5],120808:[.677,.022,.5],120809:[.656,.011,.5],120810:[.678,.022,.5],120811:[.677,.022,.5],120812:[.715,.022,.55],120813:[.716,0,.55],120814:[.716,0,.55],120815:[.716,.022,.55],120816:[.694,0,.55],120817:[.694,.022,.55],120818:[.716,.022,.55],120819:[.695,.011,.55],120820:[.715,.022,.55],120821:[.716,.022,.55],120822:[.621,.01,.525],120823:[.622,0,.525],120824:[.622,0,.525],120825:[.622,.011,.525],120826:[.624,0,.525],120827:[.611,.01,.525],120828:[.622,.011,.525],120829:[.627,.01,.525],120830:[.621,.01,.525],120831:[.622,.011,.525]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(195);e.sansSerifBoldItalic=n.AddCSS(i.sansSerifBoldItalic,{32:{c:" "},33:{c:"!"},35:{c:"#"},36:{c:"$"},37:{c:"%"},38:{c:"&"},40:{c:"("},41:{c:")"},42:{c:"*"},43:{c:"+"},44:{c:","},45:{c:"-"},46:{c:"."},47:{c:"/"},48:{c:"0"},49:{c:"1"},50:{c:"2"},51:{c:"3"},52:{c:"4"},53:{c:"5"},54:{c:"6"},55:{c:"7"},56:{c:"8"},57:{c:"9"},58:{c:":"},59:{c:";"},61:{c:"="},63:{c:"?"},64:{c:"@"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},91:{c:"["},93:{c:"]"},94:{c:"^"},95:{c:"_"},97:{c:"a"},98:{c:"b"},99:{c:"c"},100:{c:"d"},101:{c:"e"},102:{c:"f"},103:{c:"g"},104:{c:"h"},105:{c:"i"},106:{c:"j"},107:{c:"k"},108:{c:"l"},109:{c:"m"},110:{c:"n"},111:{c:"o"},112:{c:"p"},113:{c:"q"},114:{c:"r"},115:{c:"s"},116:{c:"t"},117:{c:"u"},118:{c:"v"},119:{c:"w"},120:{c:"x"},121:{c:"y"},122:{c:"z"},126:{c:"~"},913:{c:"A"},914:{c:"B"},917:{c:"E"},918:{c:"Z"},919:{c:"H"},921:{c:"I"},922:{c:"K"},924:{c:"M"},925:{c:"N"},927:{c:"O"},929:{c:"P"},930:{c:"\\398"},932:{c:"T"},935:{c:"X"},978:{c:"\\3A5"},988:{c:"F"},8213:{c:"\\2014"},8215:{c:"_"},8260:{c:"/"},8710:{c:"\\394"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.sansSerifBoldItalic={32:[0,0,.25],33:[.694,0,.319],34:[.694,-.471,.5],35:[.694,.194,.833],36:[.75,.056,.5,{ic:.065}],37:[.75,.056,.833],38:[.716,.022,.758],39:[.694,-.471,.278,{ic:.057}],40:[.75,.25,.389,{ic:.102}],41:[.75,.25,.389],42:[.75,-.306,.5,{ic:.068}],43:[.583,.083,.778],44:[.098,.125,.278],45:[.259,-.186,.333],46:[.098,0,.278],47:[.75,.25,.5,{ic:.1}],48:[.678,.022,.5],49:[.678,0,.5],50:[.678,0,.5,{ic:.051}],51:[.678,.022,.5],52:[.656,0,.5],53:[.656,.022,.5,{ic:.055}],54:[.678,.022,.5],55:[.656,.011,.5,{ic:.096}],56:[.678,.022,.5,{ic:.054}],57:[.677,.022,.5],58:[.444,0,.278],59:[.444,.125,.278],61:[.37,-.13,.778],63:[.704,0,.472,{ic:.064}],64:[.705,.01,.667],65:[.694,0,.667],66:[.694,0,.667],67:[.705,.01,.639,{ic:.08}],68:[.694,0,.722],69:[.691,0,.597,{ic:.091}],70:[.691,0,.569,{ic:.104}],71:[.705,.011,.667,{ic:.063}],72:[.694,0,.708,{ic:.06}],73:[.694,0,.278,{ic:.06}],74:[.694,.022,.472,{ic:.063}],75:[.694,0,.694,{ic:.091}],76:[.694,0,.542],77:[.694,0,.875,{ic:.054}],78:[.694,0,.708,{ic:.058}],79:[.716,.022,.736],80:[.694,0,.639,{ic:.051}],81:[.716,.125,.736],82:[.694,0,.646,{ic:.052}],83:[.716,.022,.556,{ic:.053}],84:[.688,0,.681,{ic:.109}],85:[.694,.022,.688,{ic:.059}],86:[.694,0,.667,{ic:.132}],87:[.694,0,.944,{ic:.132}],88:[.694,0,.667,{ic:.091}],89:[.694,0,.667,{ic:.143}],90:[.694,0,.611,{ic:.091}],91:[.75,.25,.289,{ic:.136}],93:[.75,.25,.289,{ic:.064}],94:[.694,-.527,.5],95:[-.038,.114,.5,{ic:.065}],97:[.461,.01,.481],98:[.694,.011,.517],99:[.46,.011,.444,{ic:.055}],100:[.694,.01,.517,{ic:.071}],101:[.46,.011,.444],102:[.705,0,.306,{ic:.188}],103:[.455,.206,.5,{ic:.068}],104:[.694,0,.517],105:[.68,0,.239,{ic:.076}],106:[.68,.204,.267,{ic:.069}],107:[.694,0,.489,{ic:.054}],108:[.694,0,.239,{ic:.072}],109:[.455,0,.794],110:[.454,0,.517],111:[.461,.011,.5],112:[.455,.194,.517],113:[.455,.194,.517],114:[.455,0,.342,{ic:.082}],115:[.461,.011,.383,{ic:.053}],116:[.571,.011,.361],117:[.444,.01,.517],118:[.444,0,.461,{ic:.079}],119:[.444,0,.683,{ic:.079}],120:[.444,0,.461,{ic:.076}],121:[.444,.205,.461,{ic:.079}],122:[.444,0,.435,{ic:.059}],126:[.327,-.193,.5,{ic:.06}],160:[0,0,.25],305:[.444,0,.239],567:[.444,.204,.267],768:[.694,-.527,0],769:[.694,-.527,0,{ic:.063}],770:[.694,-.527,0],771:[.677,-.543,0,{ic:.06}],772:[.631,-.552,0,{ic:.064}],774:[.694,-.508,0,{ic:.073}],775:[.68,-.576,0],776:[.68,-.582,0],778:[.693,-.527,0],779:[.694,-.527,0,{ic:.063}],780:[.654,-.487,0,{ic:.06}],913:[.694,0,.667],914:[.694,0,.667],915:[.691,0,.542,{ic:.104}],916:[.694,0,.833],917:[.691,0,.597,{ic:.091}],918:[.694,0,.611,{ic:.091}],919:[.694,0,.708,{ic:.06}],920:[.715,.022,.778],921:[.694,0,.278,{ic:.06}],922:[.694,0,.694,{ic:.091}],923:[.694,0,.611],924:[.694,0,.875,{ic:.054}],925:[.694,0,.708,{ic:.058}],926:[.688,0,.667,{ic:.098}],927:[.716,.022,.736],928:[.691,0,.708,{ic:.06}],929:[.694,0,.639,{ic:.051}],930:[.715,.022,.778],931:[.694,0,.722,{ic:.091}],932:[.688,0,.681,{ic:.109}],933:[.716,0,.778,{ic:.065}],934:[.694,0,.722],935:[.694,0,.667,{ic:.091}],936:[.694,0,.778,{ic:.076}],937:[.716,0,.722],978:[.716,0,.778,{ic:.065}],988:[.691,0,.569,{ic:.104}],8211:[.312,-.236,.5,{ic:.065}],8212:[.312,-.236,1,{ic:.065}],8213:[.312,-.236,1,{ic:.065}],8215:[-.038,.114,.5,{ic:.065}],8216:[.694,-.471,.278,{ic:.058}],8217:[.694,-.471,.278,{ic:.057}],8220:[.694,-.471,.5,{ic:.114}],8221:[.694,-.471,.5],8260:[.75,.25,.5,{ic:.1}],8710:[.694,0,.833]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(197);e.sansSerifBold=n.AddCSS(i.sansSerifBold,{32:{c:" "},33:{c:"!"},35:{c:"#"},36:{c:"$"},37:{c:"%"},38:{c:"&"},40:{c:"("},41:{c:")"},42:{c:"*"},43:{c:"+"},44:{c:","},45:{c:"-"},46:{c:"."},47:{c:"/"},48:{c:"0"},49:{c:"1"},50:{c:"2"},51:{c:"3"},52:{c:"4"},53:{c:"5"},54:{c:"6"},55:{c:"7"},56:{c:"8"},57:{c:"9"},58:{c:":"},59:{c:";"},61:{c:"="},63:{c:"?"},64:{c:"@"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},91:{c:"["},93:{c:"]"},94:{c:"^"},95:{c:"_"},97:{c:"a"},98:{c:"b"},99:{c:"c"},100:{c:"d"},101:{c:"e"},102:{c:"f"},103:{c:"g"},104:{c:"h"},105:{c:"i"},106:{c:"j"},107:{c:"k"},108:{c:"l"},109:{c:"m"},110:{c:"n"},111:{c:"o"},112:{c:"p"},113:{c:"q"},114:{c:"r"},115:{c:"s"},116:{c:"t"},117:{c:"u"},118:{c:"v"},119:{c:"w"},120:{c:"x"},121:{c:"y"},122:{c:"z"},126:{c:"~"},913:{c:"A"},914:{c:"B"},917:{c:"E"},918:{c:"Z"},919:{c:"H"},921:{c:"I"},922:{c:"K"},924:{c:"M"},925:{c:"N"},927:{c:"O"},929:{c:"P"},930:{c:"\\398"},932:{c:"T"},935:{c:"X"},978:{c:"\\3A5"},988:{c:"F"},8213:{c:"\\2014"},8215:{c:"_"},8260:{c:"/"},8710:{c:"\\394"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.sansSerifBold={32:[0,0,.25],33:[.694,0,.367],34:[.694,-.442,.558],35:[.694,.193,.917],36:[.75,.056,.55],37:[.75,.056,1.029],38:[.716,.022,.831],39:[.694,-.442,.306],40:[.75,.249,.428],41:[.75,.25,.428],42:[.75,-.293,.55],43:[.617,.116,.856],44:[.146,.106,.306],45:[.273,-.186,.367],46:[.146,0,.306],47:[.75,.249,.55],48:[.715,.022,.55],49:[.716,0,.55],50:[.716,0,.55],51:[.716,.022,.55],52:[.694,0,.55],53:[.694,.022,.55],54:[.716,.022,.55],55:[.695,.011,.55],56:[.715,.022,.55],57:[.716,.022,.55],58:[.458,0,.306],59:[.458,.106,.306],61:[.407,-.094,.856],63:[.705,0,.519],64:[.704,.011,.733],65:[.694,0,.733],66:[.694,0,.733],67:[.704,.011,.703],68:[.694,0,.794],69:[.691,0,.642],70:[.691,0,.611],71:[.705,.011,.733],72:[.694,0,.794],73:[.694,0,.331],74:[.694,.022,.519],75:[.694,0,.764],76:[.694,0,.581],77:[.694,0,.978],78:[.694,0,.794],79:[.716,.022,.794],80:[.694,0,.703],81:[.716,.106,.794],82:[.694,0,.703],83:[.716,.022,.611],84:[.688,0,.733],85:[.694,.022,.764],86:[.694,0,.733],87:[.694,0,1.039],88:[.694,0,.733],89:[.694,0,.733],90:[.694,0,.672],91:[.75,.25,.343],93:[.75,.25,.343],94:[.694,-.537,.55],95:[-.023,.11,.55],97:[.475,.011,.525],98:[.694,.01,.561],99:[.475,.011,.489],100:[.694,.011,.561],101:[.474,.01,.511],102:[.705,0,.336],103:[.469,.206,.55],104:[.694,0,.561],105:[.695,0,.256],106:[.695,.205,.286],107:[.694,0,.531],108:[.694,0,.256],109:[.469,0,.867],110:[.468,0,.561],111:[.474,.011,.55],112:[.469,.194,.561],113:[.469,.194,.561],114:[.469,0,.372],115:[.474,.01,.422],116:[.589,.01,.404],117:[.458,.011,.561],118:[.458,0,.5],119:[.458,0,.744],120:[.458,0,.5],121:[.458,.205,.5],122:[.458,0,.476],126:[.344,-.198,.55],160:[0,0,.25],305:[.458,0,.256],567:[.458,.205,.286],768:[.694,-.537,0],769:[.694,-.537,0],770:[.694,-.537,0],771:[.694,-.548,0],772:[.66,-.56,0],774:[.694,-.552,0],775:[.695,-.596,0],776:[.695,-.595,0],778:[.694,-.538,0],779:[.694,-.537,0],780:[.657,-.5,0],913:[.694,0,.733],914:[.694,0,.733],915:[.691,0,.581],916:[.694,0,.917],917:[.691,0,.642],918:[.694,0,.672],919:[.694,0,.794],920:[.716,.022,.856],921:[.694,0,.331],922:[.694,0,.764],923:[.694,0,.672],924:[.694,0,.978],925:[.694,0,.794],926:[.688,0,.733],927:[.716,.022,.794],928:[.691,0,.794],929:[.694,0,.703],930:[.716,.022,.856],931:[.694,0,.794],932:[.688,0,.733],933:[.715,0,.856],934:[.694,0,.794],935:[.694,0,.733],936:[.694,0,.856],937:[.716,0,.794],978:[.715,0,.856],988:[.691,0,.611],8211:[.327,-.24,.55],8212:[.327,-.24,1.1],8213:[.327,-.24,1.1],8215:[-.023,.11,.55],8216:[.694,-.443,.306],8217:[.694,-.442,.306],8220:[.694,-.443,.558],8221:[.694,-.442,.558],8260:[.75,.249,.55],8710:[.694,0,.917]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(199);e.sansSerifItalic=n.AddCSS(i.sansSerifItalic,{32:{c:" "},33:{c:"!"},35:{c:"#"},36:{c:"$"},37:{c:"%"},38:{c:"&"},40:{c:"("},41:{c:")"},42:{c:"*"},43:{c:"+"},44:{c:","},45:{c:"-"},46:{c:"."},47:{c:"/"},48:{c:"0"},49:{c:"1"},50:{c:"2"},51:{c:"3"},52:{c:"4"},53:{c:"5"},54:{c:"6"},55:{c:"7"},56:{c:"8"},57:{c:"9"},58:{c:":"},59:{c:";"},61:{c:"="},63:{c:"?"},64:{c:"@"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},91:{c:"["},93:{c:"]"},94:{c:"^"},95:{c:"_"},97:{c:"a"},98:{c:"b"},99:{c:"c"},100:{c:"d"},101:{c:"e"},102:{c:"f"},103:{c:"g"},104:{c:"h"},105:{c:"i"},106:{c:"j"},107:{c:"k"},108:{c:"l"},109:{c:"m"},110:{c:"n"},111:{c:"o"},112:{c:"p"},113:{c:"q"},114:{c:"r"},115:{c:"s"},116:{c:"t"},117:{c:"u"},118:{c:"v"},119:{c:"w"},120:{c:"x"},121:{c:"y"},122:{c:"z"},126:{c:"~"},913:{c:"A"},914:{c:"B"},917:{c:"E"},918:{c:"Z"},919:{c:"H"},921:{c:"I"},922:{c:"K"},924:{c:"M"},925:{c:"N"},927:{c:"O"},929:{c:"P"},930:{c:"\\398"},932:{c:"T"},935:{c:"X"},978:{c:"\\3A5"},988:{c:"F"},8213:{c:"\\2014"},8215:{c:"_"},8260:{c:"/"},8710:{c:"\\394"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.sansSerifItalic={32:[0,0,.25],33:[.694,0,.319],34:[.694,-.471,.5],35:[.694,.194,.833],36:[.75,.056,.5,{ic:.065}],37:[.75,.056,.833],38:[.716,.022,.758],39:[.694,-.471,.278,{ic:.057}],40:[.75,.25,.389,{ic:.102}],41:[.75,.25,.389],42:[.75,-.306,.5,{ic:.068}],43:[.583,.083,.778],44:[.098,.125,.278],45:[.259,-.186,.333],46:[.098,0,.278],47:[.75,.25,.5,{ic:.1}],48:[.678,.022,.5],49:[.678,0,.5],50:[.678,0,.5,{ic:.051}],51:[.678,.022,.5],52:[.656,0,.5],53:[.656,.022,.5,{ic:.055}],54:[.678,.022,.5],55:[.656,.011,.5,{ic:.096}],56:[.678,.022,.5,{ic:.054}],57:[.677,.022,.5],58:[.444,0,.278],59:[.444,.125,.278],61:[.37,-.13,.778],63:[.704,0,.472,{ic:.064}],64:[.705,.01,.667],65:[.694,0,.667],66:[.694,0,.667],67:[.705,.01,.639,{ic:.08}],68:[.694,0,.722],69:[.691,0,.597,{ic:.091}],70:[.691,0,.569,{ic:.104}],71:[.705,.011,.667,{ic:.063}],72:[.694,0,.708,{ic:.06}],73:[.694,0,.278,{ic:.06}],74:[.694,.022,.472,{ic:.063}],75:[.694,0,.694,{ic:.091}],76:[.694,0,.542],77:[.694,0,.875,{ic:.054}],78:[.694,0,.708,{ic:.058}],79:[.716,.022,.736],80:[.694,0,.639,{ic:.051}],81:[.716,.125,.736],82:[.694,0,.646,{ic:.052}],83:[.716,.022,.556,{ic:.053}],84:[.688,0,.681,{ic:.109}],85:[.694,.022,.688,{ic:.059}],86:[.694,0,.667,{ic:.132}],87:[.694,0,.944,{ic:.132}],88:[.694,0,.667,{ic:.091}],89:[.694,0,.667,{ic:.143}],90:[.694,0,.611,{ic:.091}],91:[.75,.25,.289,{ic:.136}],93:[.75,.25,.289,{ic:.064}],94:[.694,-.527,.5],95:[-.038,.114,.5,{ic:.065}],97:[.461,.01,.481],98:[.694,.011,.517],99:[.46,.011,.444,{ic:.055}],100:[.694,.01,.517,{ic:.071}],101:[.46,.011,.444],102:[.705,0,.306,{ic:.188}],103:[.455,.206,.5,{ic:.068}],104:[.694,0,.517],105:[.68,0,.239,{ic:.076}],106:[.68,.204,.267,{ic:.069}],107:[.694,0,.489,{ic:.054}],108:[.694,0,.239,{ic:.072}],109:[.455,0,.794],110:[.454,0,.517],111:[.461,.011,.5],112:[.455,.194,.517],113:[.455,.194,.517],114:[.455,0,.342,{ic:.082}],115:[.461,.011,.383,{ic:.053}],116:[.571,.011,.361],117:[.444,.01,.517],118:[.444,0,.461,{ic:.079}],119:[.444,0,.683,{ic:.079}],120:[.444,0,.461,{ic:.076}],121:[.444,.205,.461,{ic:.079}],122:[.444,0,.435,{ic:.059}],126:[.327,-.193,.5,{ic:.06}],160:[0,0,.25],305:[.444,0,.239],567:[.444,.204,.267],768:[.694,-.527,0],769:[.694,-.527,0,{ic:.063}],770:[.694,-.527,0],771:[.677,-.543,0,{ic:.06}],772:[.631,-.552,0,{ic:.064}],774:[.694,-.508,0,{ic:.073}],775:[.68,-.576,0],776:[.68,-.582,0],778:[.693,-.527,0],779:[.694,-.527,0,{ic:.063}],780:[.654,-.487,0,{ic:.06}],913:[.694,0,.667],914:[.694,0,.667],915:[.691,0,.542,{ic:.104}],916:[.694,0,.833],917:[.691,0,.597,{ic:.091}],918:[.694,0,.611,{ic:.091}],919:[.694,0,.708,{ic:.06}],920:[.715,.022,.778],921:[.694,0,.278,{ic:.06}],922:[.694,0,.694,{ic:.091}],923:[.694,0,.611],924:[.694,0,.875,{ic:.054}],925:[.694,0,.708,{ic:.058}],926:[.688,0,.667,{ic:.098}],927:[.716,.022,.736],928:[.691,0,.708,{ic:.06}],929:[.694,0,.639,{ic:.051}],930:[.715,.022,.778],931:[.694,0,.722,{ic:.091}],932:[.688,0,.681,{ic:.109}],933:[.716,0,.778,{ic:.065}],934:[.694,0,.722],935:[.694,0,.667,{ic:.091}],936:[.694,0,.778,{ic:.076}],937:[.716,0,.722],978:[.716,0,.778,{ic:.065}],988:[.691,0,.569,{ic:.104}],8211:[.312,-.236,.5,{ic:.065}],8212:[.312,-.236,1,{ic:.065}],8213:[.312,-.236,1,{ic:.065}],8215:[-.038,.114,.5,{ic:.065}],8216:[.694,-.471,.278,{ic:.058}],8217:[.694,-.471,.278,{ic:.057}],8220:[.694,-.471,.5,{ic:.114}],8221:[.694,-.471,.5],8260:[.75,.25,.5,{ic:.1}],8710:[.694,0,.833]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(201);e.sansSerif=n.AddCSS(i.sansSerif,{32:{c:" "},33:{c:"!"},35:{c:"#"},36:{c:"$"},37:{c:"%"},38:{c:"&"},40:{c:"("},41:{c:")"},42:{c:"*"},43:{c:"+"},44:{c:","},45:{c:"-"},46:{c:"."},47:{c:"/"},48:{c:"0"},49:{c:"1"},50:{c:"2"},51:{c:"3"},52:{c:"4"},53:{c:"5"},54:{c:"6"},55:{c:"7"},56:{c:"8"},57:{c:"9"},58:{c:":"},59:{c:";"},61:{c:"="},63:{c:"?"},64:{c:"@"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},91:{c:"["},93:{c:"]"},94:{c:"^"},95:{c:"_"},97:{c:"a"},98:{c:"b"},99:{c:"c"},100:{c:"d"},101:{c:"e"},102:{c:"f"},103:{c:"g"},104:{c:"h"},105:{c:"i"},106:{c:"j"},107:{c:"k"},108:{c:"l"},109:{c:"m"},110:{c:"n"},111:{c:"o"},112:{c:"p"},113:{c:"q"},114:{c:"r"},115:{c:"s"},116:{c:"t"},117:{c:"u"},118:{c:"v"},119:{c:"w"},120:{c:"x"},121:{c:"y"},122:{c:"z"},126:{c:"~"},913:{c:"A"},914:{c:"B"},917:{c:"E"},918:{c:"Z"},919:{c:"H"},921:{c:"I"},922:{c:"K"},924:{c:"M"},925:{c:"N"},927:{c:"O"},929:{c:"P"},930:{c:"\\398"},932:{c:"T"},935:{c:"X"},978:{c:"\\3A5"},988:{c:"F"},8213:{c:"\\2014"},8215:{c:"_"},8260:{c:"/"},8710:{c:"\\394"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.sansSerif={32:[0,0,.25],33:[.694,0,.319],34:[.694,-.471,.5],35:[.694,.194,.833],36:[.75,.056,.5],37:[.75,.056,.833],38:[.716,.022,.758],39:[.694,-.471,.278],40:[.75,.25,.389],41:[.75,.25,.389],42:[.75,-.306,.5],43:[.583,.082,.778],44:[.098,.125,.278],45:[.259,-.186,.333],46:[.098,0,.278],47:[.75,.25,.5],48:[.678,.022,.5],49:[.678,0,.5],50:[.677,0,.5],51:[.678,.022,.5],52:[.656,0,.5],53:[.656,.021,.5],54:[.677,.022,.5],55:[.656,.011,.5],56:[.678,.022,.5],57:[.677,.022,.5],58:[.444,0,.278],59:[.444,.125,.278],61:[.37,-.13,.778],63:[.704,0,.472],64:[.704,.011,.667],65:[.694,0,.667],66:[.694,0,.667],67:[.705,.011,.639],68:[.694,0,.722],69:[.691,0,.597],70:[.691,0,.569],71:[.704,.011,.667],72:[.694,0,.708],73:[.694,0,.278],74:[.694,.022,.472],75:[.694,0,.694],76:[.694,0,.542],77:[.694,0,.875],78:[.694,0,.708],79:[.715,.022,.736],80:[.694,0,.639],81:[.715,.125,.736],82:[.694,0,.646],83:[.716,.022,.556],84:[.688,0,.681],85:[.694,.022,.688],86:[.694,0,.667],87:[.694,0,.944],88:[.694,0,.667],89:[.694,0,.667],90:[.694,0,.611],91:[.75,.25,.289],93:[.75,.25,.289],94:[.694,-.527,.5],95:[-.038,.114,.5],97:[.46,.01,.481],98:[.694,.011,.517],99:[.46,.01,.444],100:[.694,.01,.517],101:[.461,.01,.444],102:[.705,0,.306],103:[.455,.206,.5],104:[.694,0,.517],105:[.68,0,.239],106:[.68,.205,.267],107:[.694,0,.489],108:[.694,0,.239],109:[.455,0,.794],110:[.455,0,.517],111:[.46,.01,.5],112:[.455,.194,.517],113:[.455,.194,.517],114:[.455,0,.342],115:[.46,.01,.383],116:[.571,.01,.361],117:[.444,.01,.517],118:[.444,0,.461],119:[.444,0,.683],120:[.444,0,.461],121:[.444,.204,.461],122:[.444,0,.435],126:[.327,-.193,.5],160:[0,0,.25],305:[.444,0,.239],567:[.444,.205,.267],768:[.694,-.527,0],769:[.694,-.527,0],770:[.694,-.527,0],771:[.677,-.543,0],772:[.631,-.552,0],774:[.694,-.508,0],775:[.68,-.576,0],776:[.68,-.582,0],778:[.694,-.527,0],779:[.694,-.527,0],780:[.654,-.487,0],913:[.694,0,.667],914:[.694,0,.667],915:[.691,0,.542],916:[.694,0,.833],917:[.691,0,.597],918:[.694,0,.611],919:[.694,0,.708],920:[.716,.021,.778],921:[.694,0,.278],922:[.694,0,.694],923:[.694,0,.611],924:[.694,0,.875],925:[.694,0,.708],926:[.688,0,.667],927:[.715,.022,.736],928:[.691,0,.708],929:[.694,0,.639],930:[.716,.021,.778],931:[.694,0,.722],932:[.688,0,.681],933:[.716,0,.778],934:[.694,0,.722],935:[.694,0,.667],936:[.694,0,.778],937:[.716,0,.722],978:[.716,0,.778],988:[.691,0,.569],8211:[.312,-.236,.5],8212:[.312,-.236,1],8213:[.312,-.236,1],8215:[-.038,.114,.5],8216:[.694,-.471,.278],8217:[.694,-.471,.278],8220:[.694,-.471,.5],8221:[.694,-.471,.5],8260:[.75,.25,.5],8710:[.694,0,.833]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(203);e.scriptBold=n.AddCSS(i.scriptBold,{32:{c:" "},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},913:{c:"A",f:"B"},914:{c:"B",f:"B"},917:{c:"E",f:"B"},918:{c:"Z",f:"B"},919:{c:"H",f:"B"},921:{c:"I",f:"B"},922:{c:"K",f:"B"},924:{c:"M",f:"B"},925:{c:"N",f:"B"},927:{c:"O",f:"B"},929:{c:"P",f:"B"},930:{c:"\\398",f:"B"},932:{c:"T",f:"B"},935:{c:"X",f:"B"},978:{c:"\\3A5",f:"B"},988:{c:"F",f:"B"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.scriptBold={32:[0,0,.25],65:[.717,.008,.803,{ic:.213,sk:.389}],66:[.708,.028,.908,{sk:.194}],67:[.728,.026,.666,{ic:.153,sk:.278}],68:[.708,.031,.774,{ic:.081,sk:.111}],69:[.707,.008,.562,{ic:.156,sk:.139}],70:[.735,.036,.895,{ic:.095,sk:.222}],71:[.717,.037,.61,{ic:.128,sk:.25}],72:[.717,.036,.969,{ic:.272,sk:.333}],73:[.717,.017,.809,{ic:.137,sk:.333}],74:[.717,.314,1.052,{ic:.081,sk:.417}],75:[.717,.037,.914,{ic:.29,sk:.361}],76:[.717,.017,.874,{ic:.161,sk:.306}],77:[.721,.05,1.08,{ic:.136,sk:.444}],78:[.726,.036,.902,{ic:.306,sk:.389}],79:[.707,.008,.738,{ic:.067,sk:.167}],80:[.716,.037,1.013,{sk:.222}],81:[.717,.017,.883,{sk:.278}],82:[.717,.017,.85,{sk:.194}],83:[.708,.036,.868,{ic:.148,sk:.333}],84:[.735,.037,.747,{ic:.249,sk:.222}],85:[.717,.017,.8,{ic:.16,sk:.25}],86:[.717,.017,.622,{ic:.228,sk:.222}],87:[.717,.017,.805,{ic:.221,sk:.25}],88:[.717,.017,.944,{ic:.187,sk:.278}],89:[.716,.017,.71,{ic:.249,sk:.194}],90:[.717,.016,.821,{ic:.211,sk:.306}],160:[0,0,.25],913:[.698,0,.869],914:[.686,0,.818],917:[.68,0,.756],918:[.686,0,.703],919:[.686,0,.9],921:[.686,0,.436],922:[.686,0,.901],924:[.686,0,1.092],925:[.686,0,.9],927:[.696,.01,.864],929:[.686,0,.786],930:[.696,.01,.894],932:[.675,0,.8],935:[.686,0,.869],978:[.697,0,.894],988:[.68,0,.724]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(205);e.script=n.AddCSS(i.script,{32:{c:" "},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},913:{c:"A",f:""},914:{c:"B",f:""},917:{c:"E",f:""},918:{c:"Z",f:""},919:{c:"H",f:""},921:{c:"I",f:""},922:{c:"K",f:""},924:{c:"M",f:""},925:{c:"N",f:""},927:{c:"O",f:""},929:{c:"P",f:""},930:{c:"\\398",f:""},932:{c:"T",f:""},935:{c:"X",f:""},978:{c:"\\3A5",f:""},988:{c:"F",f:""},8459:{c:"H",f:"SC"},8464:{c:"J",f:"SC"},8466:{c:"L",f:"SC"},8475:{c:"R",f:"SC"},8492:{c:"B",f:"SC"},8496:{c:"E",f:"SC"},8497:{c:"F",f:"SC"},8499:{c:"M",f:"SC"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.script={32:[0,0,.25],65:[.717,.008,.803,{ic:.213,sk:.389}],66:[.708,.028,.908,{sk:.194}],67:[.728,.026,.666,{ic:.153,sk:.278}],68:[.708,.031,.774,{ic:.081,sk:.111}],69:[.707,.008,.562,{ic:.156,sk:.139}],70:[.735,.036,.895,{ic:.095,sk:.222}],71:[.717,.037,.61,{ic:.128,sk:.25}],72:[.717,.036,.969,{ic:.272,sk:.333}],73:[.717,.017,.809,{ic:.137,sk:.333}],74:[.717,.314,1.052,{ic:.081,sk:.417}],75:[.717,.037,.914,{ic:.29,sk:.361}],76:[.717,.017,.874,{ic:.161,sk:.306}],77:[.721,.05,1.08,{ic:.136,sk:.444}],78:[.726,.036,.902,{ic:.306,sk:.389}],79:[.707,.008,.738,{ic:.067,sk:.167}],80:[.716,.037,1.013,{sk:.222}],81:[.717,.017,.883,{sk:.278}],82:[.717,.017,.85,{sk:.194}],83:[.708,.036,.868,{ic:.148,sk:.333}],84:[.735,.037,.747,{ic:.249,sk:.222}],85:[.717,.017,.8,{ic:.16,sk:.25}],86:[.717,.017,.622,{ic:.228,sk:.222}],87:[.717,.017,.805,{ic:.221,sk:.25}],88:[.717,.017,.944,{ic:.187,sk:.278}],89:[.716,.017,.71,{ic:.249,sk:.194}],90:[.717,.016,.821,{ic:.211,sk:.306}],160:[0,0,.25],913:[.716,0,.75],914:[.683,0,.708],917:[.68,0,.681],918:[.683,0,.611],919:[.683,0,.75],921:[.683,0,.361],922:[.683,0,.778],924:[.683,0,.917],925:[.683,0,.75],927:[.705,.022,.778],929:[.683,0,.681],930:[.705,.022,.778],932:[.677,0,.722],935:[.683,0,.75],978:[.705,0,.778],988:[.68,0,.653],8459:[.717,.036,.969,{ic:.272,sk:.333}],8464:[.717,.314,1.052,{ic:.081,sk:.417}],8466:[.717,.017,.874,{ic:.161,sk:.306}],8475:[.717,.017,.85,{sk:.194}],8492:[.708,.028,.908,{sk:.194}],8496:[.707,.008,.562,{ic:.156,sk:.139}],8497:[.735,.036,.895,{ic:.095,sk:.222}],8499:[.721,.05,1.08,{ic:.136,sk:.444}]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(207);e.smallop=n.AddCSS(i.smallop,{32:{c:" "},40:{c:"("},41:{c:")"},47:{c:"/"},91:{c:"["},93:{c:"]"},123:{c:"{"},125:{c:"}"},8260:{c:"/"},9001:{c:"\\27E8"},9002:{c:"\\27E9"},10072:{c:"\\2223"},10764:{c:"\\222C\\222C"},12296:{c:"\\27E8"},12297:{c:"\\27E9"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.smallop={32:[0,0,.25],40:[.85,.349,.458],41:[.85,.349,.458],47:[.85,.349,.578],91:[.85,.349,.417],92:[.85,.349,.578],93:[.85,.349,.417],123:[.85,.349,.583],125:[.85,.349,.583],160:[0,0,.25],710:[.744,-.551,.556],732:[.722,-.597,.556],770:[.744,-.551,0],771:[.722,-.597,0],8214:[.602,0,.778],8260:[.85,.349,.578],8593:[.6,0,.667],8595:[.6,0,.667],8657:[.599,0,.778],8659:[.6,0,.778],8719:[.75,.25,.944],8720:[.75,.25,.944],8721:[.75,.25,1.056],8730:[.85,.35,1],8739:[.627,.015,.333],8741:[.627,.015,.556],8747:[.805,.306,.472,{ic:.138}],8748:[.805,.306,.819,{ic:.138}],8749:[.805,.306,1.166,{ic:.138}],8750:[.805,.306,.472,{ic:.138}],8896:[.75,.249,.833],8897:[.75,.249,.833],8898:[.75,.249,.833],8899:[.75,.249,.833],8968:[.85,.349,.472],8969:[.85,.349,.472],8970:[.85,.349,.472],8971:[.85,.349,.472],9001:[.85,.35,.472],9002:[.85,.35,.472],9168:[.602,0,.667],10072:[.627,.015,.333],10216:[.85,.35,.472],10217:[.85,.35,.472],10752:[.75,.25,1.111],10753:[.75,.25,1.111],10754:[.75,.25,1.111],10756:[.75,.249,.833],10758:[.75,.249,.833],10764:[.805,.306,1.638,{ic:.138}],12296:[.85,.35,.472],12297:[.85,.35,.472]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(209);e.texCalligraphicBold=n.AddCSS(i.texCalligraphicBold,{32:{c:" "},47:{c:"/"},48:{c:"0"},49:{c:"1"},50:{c:"2"},51:{c:"3"},52:{c:"4"},53:{c:"5"},54:{c:"6"},55:{c:"7"},56:{c:"8"},57:{c:"9"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},97:{c:"a"},98:{c:"b"},99:{c:"c"},100:{c:"d"},101:{c:"e"},102:{c:"f"},103:{c:"g"},104:{c:"h"},105:{c:"i"},106:{c:"j"},107:{c:"k"},108:{c:"l"},109:{c:"m"},110:{c:"n"},111:{c:"o"},112:{c:"p"},113:{c:"q"},114:{c:"r"},115:{c:"s"},116:{c:"t"},117:{c:"u"},118:{c:"v"},119:{c:"w"},120:{c:"x"},121:{c:"y"},122:{c:"z"},913:{c:"A",f:"BI"},914:{c:"B",f:"BI"},917:{c:"E",f:"BI"},918:{c:"Z",f:"BI"},919:{c:"H",f:"BI"},921:{c:"I",f:"BI"},922:{c:"K",f:"BI"},924:{c:"M",f:"BI"},925:{c:"N",f:"BI"},927:{c:"O",f:"BI"},929:{c:"P",f:"BI"},930:{c:"\\398",f:"BI"},932:{c:"T",f:"BI"},935:{c:"X",f:"BI"},978:{c:"\\3A5",f:"BI"},988:{c:"F",f:"BI"},8260:{c:"/"},8710:{c:"\\394"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.texCalligraphicBold={32:[0,0,.25],47:[.711,.21,.894],48:[.46,.017,.575],49:[.461,0,.575],50:[.46,0,.575],51:[.461,.211,.575],52:[.469,.194,.575],53:[.461,.211,.575],54:[.66,.017,.575],55:[.476,.211,.575],56:[.661,.017,.575],57:[.461,.21,.575],65:[.751,.049,.921,{ic:.068,sk:.224}],66:[.705,.017,.748,{sk:.16}],67:[.703,.02,.613,{sk:.16}],68:[.686,0,.892,{sk:.0958}],69:[.703,.016,.607,{sk:.128}],70:[.686,.03,.814,{ic:.116,sk:.128}],71:[.703,.113,.682,{sk:.128}],72:[.686,.048,.987,{sk:.128}],73:[.686,0,.642,{ic:.104,sk:.0319}],74:[.686,.114,.779,{ic:.158,sk:.192}],75:[.703,.017,.871,{sk:.0639}],76:[.703,.017,.788,{sk:.16}],77:[.703,.049,1.378,{sk:.16}],78:[.84,.049,.937,{ic:.168,sk:.0958}],79:[.703,.017,.906,{sk:.128}],80:[.686,.067,.81,{sk:.0958}],81:[.703,.146,.939,{sk:.128}],82:[.686,.017,.99,{sk:.0958}],83:[.703,.016,.696,{sk:.16}],84:[.72,.069,.644,{ic:.303,sk:.0319}],85:[.686,.024,.715,{ic:.056,sk:.0958}],86:[.686,.077,.737,{sk:.0319}],87:[.686,.077,1.169,{sk:.0958}],88:[.686,0,.817,{ic:.089,sk:.16}],89:[.686,.164,.759,{sk:.0958}],90:[.686,0,.818,{sk:.16}],97:[.452,.008,.633],98:[.694,.008,.521],99:[.451,.008,.513,{sk:.0639}],100:[.694,.008,.61,{sk:.192}],101:[.452,.008,.554,{sk:.0639}],102:[.701,.201,.568,{ic:.056,sk:.192}],103:[.452,.202,.545,{sk:.0319}],104:[.694,.008,.668,{sk:-.0319}],105:[.694,.008,.405],106:[.694,.202,.471],107:[.694,.008,.604],108:[.694,.008,.348,{sk:.0958}],109:[.452,.008,1.032],110:[.452,.008,.713],111:[.452,.008,.585,{sk:.0639}],112:[.452,.194,.601,{sk:.0958}],113:[.452,.194,.542,{sk:.0958}],114:[.452,.008,.529,{sk:.0639}],115:[.451,.008,.531,{sk:.0639}],116:[.643,.007,.415,{sk:.0958}],117:[.452,.008,.681,{sk:.0319}],118:[.453,.008,.567,{sk:.0319}],119:[.453,.008,.831,{sk:.0958}],120:[.452,.008,.659,{sk:.0319}],121:[.452,.202,.59,{sk:.0639}],122:[.452,.008,.555,{sk:.0639}],160:[0,0,.25],913:[.711,0,.869,{sk:.16}],914:[.686,0,.866,{sk:.0958}],915:[.68,0,.657,{ic:.12,sk:.0958}],916:[.711,0,.958,{sk:.192}],917:[.68,0,.81,{sk:.0958}],918:[.686,0,.773,{sk:.0958}],919:[.686,0,.982,{sk:.0639}],920:[.702,.017,.867,{sk:.0958}],921:[.686,0,.511,{ic:.062,sk:.128}],922:[.686,0,.971,{sk:.0639}],923:[.711,0,.806,{sk:.192}],924:[.686,0,1.142,{ic:.077,sk:.0958}],925:[.686,0,.95,{ic:.077,sk:.0958}],926:[.675,0,.841,{sk:.0958}],927:[.703,.017,.837,{sk:.0958}],928:[.68,0,.982,{sk:.0639}],929:[.686,0,.723,{ic:.124,sk:.0958}],930:[.702,.017,.867,{sk:.0958}],931:[.686,0,.885,{sk:.0958}],932:[.675,0,.637,{ic:.135,sk:.0958}],933:[.703,0,.671,{ic:.131,sk:.0639}],934:[.686,0,.767,{sk:.0958}],935:[.686,0,.947,{sk:.0958}],936:[.686,0,.714,{ic:.076,sk:.0639}],937:[.703,0,.879,{sk:.0958}],945:[.452,.008,.761,{sk:.0319}],946:[.701,.194,.66,{sk:.0958}],947:[.451,.211,.59],948:[.725,.008,.522,{sk:.0639}],949:[.461,.017,.529,{sk:.0958}],950:[.711,.202,.508,{sk:.0958}],951:[.452,.211,.6,{sk:.0639}],952:[.702,.008,.562,{sk:.0958}],953:[.452,.008,.412,{sk:.0639}],954:[.452,.008,.668],955:[.694,.013,.671],956:[.452,.211,.708,{sk:.0319}],957:[.452,0,.577,{sk:.0319}],958:[.711,.201,.508,{sk:.128}],959:[.452,.008,.585,{sk:.0639}],960:[.444,.008,.682],961:[.451,.211,.612,{sk:.0958}],962:[.451,.105,.424,{sk:.0958}],963:[.444,.008,.686],964:[.444,.013,.521,{ic:.089,sk:.0319}],965:[.453,.008,.631,{sk:.0319}],966:[.452,.216,.747,{sk:.0958}],967:[.452,.201,.718,{sk:.0639}],968:[.694,.202,.758,{sk:.128}],969:[.453,.008,.718],977:[.701,.008,.692,{sk:.0958}],978:[.703,0,.671,{ic:.131,sk:.0639}],981:[.694,.202,.712,{sk:.0958}],982:[.444,.008,.975],988:[.68,0,.689,{ic:.12,sk:.0958}],1009:[.451,.194,.612,{sk:.0958}],1013:[.444,.007,.483,{sk:.0639}],8260:[.711,.21,.894],8710:[.711,0,.958,{sk:.192}]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(211);e.texCalligraphic=n.AddCSS(i.texCalligraphic,{32:{c:" "},48:{c:"0"},49:{c:"1"},50:{c:"2"},51:{c:"3"},52:{c:"4"},53:{c:"5"},54:{c:"6"},55:{c:"7"},56:{c:"8"},57:{c:"9"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},913:{c:"A",f:"I"},914:{c:"B",f:"I"},917:{c:"E",f:"I"},918:{c:"Z",f:"I"},919:{c:"H",f:"I"},921:{c:"I",f:"I"},922:{c:"K",f:"I"},924:{c:"M",f:"I"},925:{c:"N",f:"I"},927:{c:"O",f:"I"},929:{c:"P",f:"I"},930:{c:"\\398",f:"I"},932:{c:"T",f:"I"},935:{c:"X",f:"I"},978:{c:"\\3A5",f:"I"},988:{c:"F",f:"I"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.texCalligraphic={32:[0,0,.25],48:[.452,.022,.5],49:[.453,0,.5],50:[.453,0,.5],51:[.452,.216,.5],52:[.464,.194,.5],53:[.453,.216,.5],54:[.665,.022,.5],55:[.463,.216,.5],56:[.666,.021,.5],57:[.453,.216,.5],65:[.728,.05,.798,{sk:.194}],66:[.705,.022,.657,{sk:.139}],67:[.705,.025,.527,{sk:.139}],68:[.683,0,.771,{sk:.0833}],69:[.705,.022,.528,{sk:.111}],70:[.683,.032,.719,{ic:.11,sk:.111}],71:[.704,.119,.595,{sk:.111}],72:[.683,.048,.845,{sk:.111}],73:[.683,0,.545,{ic:.097,sk:.0278}],74:[.683,.119,.678,{ic:.161,sk:.167}],75:[.705,.022,.762,{sk:.0556}],76:[.705,.022,.69,{sk:.139}],77:[.705,.05,1.201,{sk:.139}],78:[.789,.05,.82,{ic:.159,sk:.0833}],79:[.705,.022,.796,{sk:.111}],80:[.683,.057,.696,{sk:.0833}],81:[.705,.131,.817,{sk:.111}],82:[.682,.022,.848,{sk:.0833}],83:[.705,.022,.606,{sk:.139}],84:[.717,.068,.545,{ic:.288,sk:.0278}],85:[.683,.028,.626,{ic:.061,sk:.0833}],86:[.683,.052,.613,{sk:.0278}],87:[.683,.053,.988,{sk:.0833}],88:[.683,0,.713,{ic:.094,sk:.139}],89:[.683,.143,.668,{sk:.0833}],90:[.683,0,.725,{sk:.139}],160:[0,0,.25],913:[.716,0,.75,{sk:.139}],914:[.683,0,.759,{sk:.0833}],917:[.68,0,.738,{sk:.0833}],918:[.683,0,.683,{sk:.0833}],919:[.683,0,.831,{ic:.057,sk:.0556}],921:[.683,0,.44,{ic:.064,sk:.111}],922:[.683,0,.849,{sk:.0556}],924:[.683,0,.97,{ic:.081,sk:.0833}],925:[.683,0,.803,{ic:.085,sk:.0833}],927:[.704,.022,.763,{sk:.0833}],929:[.683,0,.642,{ic:.109,sk:.0833}],930:[.704,.022,.763,{sk:.0833}],932:[.677,0,.584,{ic:.12,sk:.0833}],935:[.683,0,.828,{sk:.0833}],978:[.705,0,.583,{ic:.117,sk:.0556}],988:[.68,0,.643,{ic:.106,sk:.0833}]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(213);e.texMathit=n.AddCSS(i.texMathit,{32:{c:" "},33:{c:"!"},35:{c:"#"},37:{c:"%"},38:{c:"&"},40:{c:"("},41:{c:")"},42:{c:"*"},43:{c:"+"},44:{c:","},45:{c:"-"},46:{c:"."},47:{c:"/"},48:{c:"0"},49:{c:"1"},50:{c:"2"},51:{c:"3"},52:{c:"4"},53:{c:"5"},54:{c:"6"},55:{c:"7"},56:{c:"8"},57:{c:"9"},58:{c:":"},59:{c:";"},61:{c:"="},63:{c:"?"},64:{c:"@"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},91:{c:"["},93:{c:"]"},94:{c:"^"},95:{c:"_"},97:{c:"a"},98:{c:"b"},99:{c:"c"},100:{c:"d"},101:{c:"e"},102:{c:"f"},103:{c:"g"},104:{c:"h"},105:{c:"i"},106:{c:"j"},107:{c:"k"},108:{c:"l"},109:{c:"m"},110:{c:"n"},111:{c:"o"},112:{c:"p"},113:{c:"q"},114:{c:"r"},115:{c:"s"},116:{c:"t"},117:{c:"u"},118:{c:"v"},119:{c:"w"},120:{c:"x"},121:{c:"y"},122:{c:"z"},126:{c:"~"},913:{c:"A"},914:{c:"B"},917:{c:"E"},918:{c:"Z"},919:{c:"H"},921:{c:"I"},922:{c:"K"},924:{c:"M"},925:{c:"N"},927:{c:"O"},929:{c:"P"},930:{c:"\\398"},932:{c:"T"},935:{c:"X"},978:{c:"\\3A5"},988:{c:"F"},8213:{c:"\\2014"},8215:{c:"_"},8260:{c:"/"},8710:{c:"\\394"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.texMathit={32:[0,0,.25],33:[.716,0,.307,{ic:.073}],34:[.694,-.379,.514],35:[.694,.194,.818],37:[.75,.056,.818],38:[.716,.022,.767],39:[.694,-.379,.307,{ic:.07}],40:[.75,.25,.409,{ic:.108}],41:[.75,.25,.409],42:[.75,-.32,.511,{ic:.073}],43:[.557,.057,.767],44:[.121,.194,.307],45:[.251,-.18,.358],46:[.121,0,.307],47:[.75,.25,.511,{ic:.106}],48:[.665,.021,.511,{ic:.051}],49:[.666,0,.511],50:[.666,.022,.511],51:[.666,.022,.511,{ic:.051}],52:[.666,.194,.511],53:[.666,.022,.511,{ic:.056}],54:[.665,.022,.511,{ic:.054}],55:[.666,.022,.511,{ic:.123}],56:[.666,.021,.511],57:[.666,.022,.511],58:[.431,0,.307],59:[.431,.194,.307],61:[.367,-.133,.767],63:[.716,0,.511],64:[.705,.011,.767],65:[.716,0,.743],66:[.683,0,.704],67:[.705,.021,.716,{ic:.096}],68:[.683,0,.755],69:[.68,0,.678,{ic:.065}],70:[.68,0,.653,{ic:.078}],71:[.705,.022,.774],72:[.683,0,.743,{ic:.117}],73:[.683,0,.386,{ic:.122}],74:[.683,.021,.525,{ic:.097}],75:[.683,0,.769,{ic:.09}],76:[.683,0,.627],77:[.683,0,.897,{ic:.113}],78:[.683,0,.743,{ic:.117}],79:[.704,.022,.767],80:[.683,0,.678,{ic:.051}],81:[.704,.194,.767],82:[.683,.022,.729],83:[.705,.022,.562,{ic:.071}],84:[.677,0,.716,{ic:.09}],85:[.683,.022,.743,{ic:.117}],86:[.683,.022,.743,{ic:.125}],87:[.683,.022,.999,{ic:.125}],88:[.683,0,.743,{ic:.082}],89:[.683,0,.743,{ic:.132}],90:[.683,0,.613,{ic:.091}],91:[.75,.25,.307,{ic:.139}],93:[.75,.25,.307,{ic:.052}],94:[.694,-.527,.511],95:[-.025,.062,.511],97:[.442,.011,.511],98:[.694,.011,.46],99:[.441,.01,.46],100:[.694,.011,.511,{ic:.056}],101:[.442,.01,.46],102:[.705,.204,.307,{ic:.143}],103:[.442,.205,.46],104:[.694,.011,.511],105:[.656,.01,.307],106:[.656,.204,.307,{ic:.057}],107:[.694,.011,.46],108:[.694,.011,.256,{ic:.056}],109:[.442,.011,.818],110:[.442,.011,.562],111:[.442,.011,.511],112:[.442,.194,.511],113:[.442,.194,.46],114:[.442,.011,.422,{ic:.062}],115:[.442,.011,.409],116:[.626,.011,.332],117:[.441,.011,.537],118:[.443,.01,.46],119:[.443,.011,.664],120:[.442,.011,.464],121:[.441,.205,.486],122:[.442,.011,.409,{ic:.057}],126:[.318,-.208,.511,{ic:.06}],160:[0,0,.25],163:[.714,.011,.769],305:[.441,.01,.307],567:[.442,.204,.332],768:[.697,-.5,0],769:[.697,-.5,0],770:[.694,-.527,0],771:[.668,-.558,0,{ic:.06}],772:[.589,-.544,0,{ic:.054}],774:[.694,-.515,0,{ic:.062}],775:[.669,-.548,0],776:[.669,-.554,0],778:[.716,-.542,0],779:[.697,-.503,0,{ic:.065}],780:[.638,-.502,0],913:[.716,0,.743],914:[.683,0,.704],915:[.68,0,.627,{ic:.078}],916:[.716,0,.818],917:[.68,0,.678,{ic:.065}],918:[.683,0,.613,{ic:.091}],919:[.683,0,.743,{ic:.117}],920:[.704,.022,.767],921:[.683,0,.386,{ic:.122}],922:[.683,0,.769,{ic:.09}],923:[.716,0,.692],924:[.683,0,.897,{ic:.113}],925:[.683,0,.743,{ic:.117}],926:[.677,0,.664,{ic:.09}],927:[.704,.022,.767],928:[.68,0,.743,{ic:.116}],929:[.683,0,.678,{ic:.051}],930:[.704,.022,.767],931:[.683,0,.716,{ic:.066}],932:[.677,0,.716,{ic:.09}],933:[.705,0,.767,{ic:.065}],934:[.683,0,.716],935:[.683,0,.743,{ic:.082}],936:[.683,0,.767,{ic:.057}],937:[.705,0,.716],978:[.705,0,.767,{ic:.065}],988:[.68,0,.653,{ic:.078}],8211:[.285,-.248,.511],8212:[.285,-.248,1.022],8213:[.285,-.248,1.022],8215:[-.025,.062,.511],8216:[.694,-.379,.307,{ic:.055}],8217:[.694,-.379,.307,{ic:.07}],8220:[.694,-.379,.514,{ic:.092}],8221:[.694,-.379,.514],8260:[.75,.25,.511,{ic:.106}],8463:[.695,.013,.54],8710:[.716,0,.818]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(215);e.texOldstyleBold=n.AddCSS(i.texOldstyleBold,{32:{c:" "},48:{c:"0"},49:{c:"1"},50:{c:"2"},51:{c:"3"},52:{c:"4"},53:{c:"5"},54:{c:"6"},55:{c:"7"},56:{c:"8"},57:{c:"9"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},913:{c:"A",f:"B"},914:{c:"B",f:"B"},917:{c:"E",f:"B"},918:{c:"Z",f:"B"},919:{c:"H",f:"B"},921:{c:"I",f:"B"},922:{c:"K",f:"B"},924:{c:"M",f:"B"},925:{c:"N",f:"B"},927:{c:"O",f:"B"},929:{c:"P",f:"B"},930:{c:"\\398",f:"B"},932:{c:"T",f:"B"},935:{c:"X",f:"B"},978:{c:"\\3A5",f:"B"},988:{c:"F",f:"B"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.texOldstyleBold={32:[0,0,.25],48:[.46,.017,.575],49:[.461,0,.575],50:[.46,0,.575],51:[.461,.211,.575],52:[.469,.194,.575],53:[.461,.211,.575],54:[.66,.017,.575],55:[.476,.211,.575],56:[.661,.017,.575],57:[.461,.21,.575],65:[.751,.049,.921,{ic:.068,sk:.224}],66:[.705,.017,.748,{sk:.16}],67:[.703,.02,.613,{sk:.16}],68:[.686,0,.892,{sk:.0958}],69:[.703,.016,.607,{sk:.128}],70:[.686,.03,.814,{ic:.116,sk:.128}],71:[.703,.113,.682,{sk:.128}],72:[.686,.048,.987,{sk:.128}],73:[.686,0,.642,{ic:.104,sk:.0319}],74:[.686,.114,.779,{ic:.158,sk:.192}],75:[.703,.017,.871,{sk:.0639}],76:[.703,.017,.788,{sk:.16}],77:[.703,.049,1.378,{sk:.16}],78:[.84,.049,.937,{ic:.168,sk:.0958}],79:[.703,.017,.906,{sk:.128}],80:[.686,.067,.81,{sk:.0958}],81:[.703,.146,.939,{sk:.128}],82:[.686,.017,.99,{sk:.0958}],83:[.703,.016,.696,{sk:.16}],84:[.72,.069,.644,{ic:.303,sk:.0319}],85:[.686,.024,.715,{ic:.056,sk:.0958}],86:[.686,.077,.737,{sk:.0319}],87:[.686,.077,1.169,{sk:.0958}],88:[.686,0,.817,{ic:.089,sk:.16}],89:[.686,.164,.759,{sk:.0958}],90:[.686,0,.818,{sk:.16}],160:[0,0,.25],913:[.698,0,.869],914:[.686,0,.818],917:[.68,0,.756],918:[.686,0,.703],919:[.686,0,.9],921:[.686,0,.436],922:[.686,0,.901],924:[.686,0,1.092],925:[.686,0,.9],927:[.696,.01,.864],929:[.686,0,.786],930:[.696,.01,.894],932:[.675,0,.8],935:[.686,0,.869],978:[.697,0,.894],988:[.68,0,.724]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(217);e.texOldstyle=n.AddCSS(i.texOldstyle,{32:{c:" "},48:{c:"0"},49:{c:"1"},50:{c:"2"},51:{c:"3"},52:{c:"4"},53:{c:"5"},54:{c:"6"},55:{c:"7"},56:{c:"8"},57:{c:"9"},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},913:{c:"A",f:""},914:{c:"B",f:""},917:{c:"E",f:""},918:{c:"Z",f:""},919:{c:"H",f:""},921:{c:"I",f:""},922:{c:"K",f:""},924:{c:"M",f:""},925:{c:"N",f:""},927:{c:"O",f:""},929:{c:"P",f:""},930:{c:"\\398",f:""},932:{c:"T",f:""},935:{c:"X",f:""},978:{c:"\\3A5",f:""},988:{c:"F",f:""}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.texOldstyle={32:[0,0,.25],48:[.452,.022,.5],49:[.453,0,.5],50:[.453,0,.5],51:[.452,.216,.5],52:[.464,.194,.5],53:[.453,.216,.5],54:[.665,.022,.5],55:[.463,.216,.5],56:[.666,.021,.5],57:[.453,.216,.5],65:[.728,.05,.798,{sk:.194}],66:[.705,.022,.657,{sk:.139}],67:[.705,.025,.527,{sk:.139}],68:[.683,0,.771,{sk:.0833}],69:[.705,.022,.528,{sk:.111}],70:[.683,.032,.719,{ic:.11,sk:.111}],71:[.704,.119,.595,{sk:.111}],72:[.683,.048,.845,{sk:.111}],73:[.683,0,.545,{ic:.097,sk:.0278}],74:[.683,.119,.678,{ic:.161,sk:.167}],75:[.705,.022,.762,{sk:.0556}],76:[.705,.022,.69,{sk:.139}],77:[.705,.05,1.201,{sk:.139}],78:[.789,.05,.82,{ic:.159,sk:.0833}],79:[.705,.022,.796,{sk:.111}],80:[.683,.057,.696,{sk:.0833}],81:[.705,.131,.817,{sk:.111}],82:[.682,.022,.848,{sk:.0833}],83:[.705,.022,.606,{sk:.139}],84:[.717,.068,.545,{ic:.288,sk:.0278}],85:[.683,.028,.626,{ic:.061,sk:.0833}],86:[.683,.052,.613,{sk:.0278}],87:[.683,.053,.988,{sk:.0833}],88:[.683,0,.713,{ic:.094,sk:.139}],89:[.683,.143,.668,{sk:.0833}],90:[.683,0,.725,{sk:.139}],160:[0,0,.25],913:[.716,0,.75],914:[.683,0,.708],917:[.68,0,.681],918:[.683,0,.611],919:[.683,0,.75],921:[.683,0,.361],922:[.683,0,.778],924:[.683,0,.917],925:[.683,0,.75],927:[.705,.022,.778],929:[.683,0,.681],930:[.705,.022,.778],932:[.677,0,.722],935:[.683,0,.75],978:[.705,0,.778],988:[.68,0,.653]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(219);e.texSize3=n.AddCSS(i.texSize3,{32:{c:" "},40:{c:"("},41:{c:")"},47:{c:"/"},91:{c:"["},93:{c:"]"},123:{c:"{"},125:{c:"}"},8260:{c:"/"},9001:{c:"\\27E8"},9002:{c:"\\27E9"},12296:{c:"\\27E8"},12297:{c:"\\27E9"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.texSize3={32:[0,0,.25],40:[1.45,.949,.736],41:[1.45,.949,.736],47:[1.45,.949,1.044],91:[1.45,.949,.528],92:[1.45,.949,1.044],93:[1.45,.949,.528],123:[1.45,.949,.75],125:[1.45,.949,.75],160:[0,0,.25],710:[.772,-.564,1.444],732:[.749,-.61,1.444],770:[.772,-.564,0],771:[.749,-.61,0],8260:[1.45,.949,1.044],8730:[1.45,.95,1],8968:[1.45,.949,.583],8969:[1.45,.949,.583],8970:[1.45,.949,.583],8971:[1.45,.949,.583],9001:[1.45,.95,.75],9002:[1.45,.949,.75],10216:[1.45,.95,.75],10217:[1.45,.949,.75],12296:[1.45,.95,.75],12297:[1.45,.949,.75]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(221);e.texSize4=n.AddCSS(i.texSize4,{32:{c:" "},40:{c:"("},41:{c:")"},47:{c:"/"},91:{c:"["},93:{c:"]"},123:{c:"{"},125:{c:"}"},8260:{c:"/"},9001:{c:"\\27E8"},9002:{c:"\\27E9"},12296:{c:"\\27E8"},12297:{c:"\\27E9"},57685:{c:"\\E153\\E152"},57686:{c:"\\E151\\E150"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.texSize4={32:[0,0,.25],40:[1.75,1.249,.792],41:[1.75,1.249,.792],47:[1.75,1.249,1.278],91:[1.75,1.249,.583],92:[1.75,1.249,1.278],93:[1.75,1.249,.583],123:[1.75,1.249,.806],125:[1.75,1.249,.806],160:[0,0,.25],710:[.845,-.561,1.889],732:[.823,-.583,1.889],770:[.845,-.561,0],771:[.823,-.583,0],8260:[1.75,1.249,1.278],8730:[1.75,1.25,1],8968:[1.75,1.249,.639],8969:[1.75,1.249,.639],8970:[1.75,1.249,.639],8971:[1.75,1.249,.639],9001:[1.75,1.248,.806],9002:[1.75,1.248,.806],9115:[1.154,.655,.875],9116:[.61,.01,.875],9117:[1.165,.644,.875],9118:[1.154,.655,.875],9119:[.61,.01,.875],9120:[1.165,.644,.875],9121:[1.154,.645,.667],9122:[.602,0,.667],9123:[1.155,.644,.667],9124:[1.154,.645,.667],9125:[.602,0,.667],9126:[1.155,.644,.667],9127:[.899,.01,.889],9128:[1.16,.66,.889],9129:[.01,.899,.889],9130:[.29,.015,.889],9131:[.899,.01,.889],9132:[1.16,.66,.889],9133:[.01,.899,.889],9143:[.935,.885,1.056],10216:[1.75,1.248,.806],10217:[1.75,1.248,.806],12296:[1.75,1.248,.806],12297:[1.75,1.248,.806],57344:[.625,.014,1.056],57345:[.605,.014,1.056],57680:[.12,.213,.45],57681:[.12,.213,.45],57682:[.333,0,.45],57683:[.333,0,.45],57684:[.32,.2,.4],57685:[.333,0,.9],57686:[.12,.213,.9]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(1),i=r(223);e.texVariant=n.AddCSS(i.texVariant,{32:{c:" "},65:{c:"A"},66:{c:"B"},67:{c:"C"},68:{c:"D"},69:{c:"E"},70:{c:"F"},71:{c:"G"},72:{c:"H"},73:{c:"I"},74:{c:"J"},75:{c:"K"},76:{c:"L"},77:{c:"M"},78:{c:"N"},79:{c:"O"},80:{c:"P"},81:{c:"Q"},82:{c:"R"},83:{c:"S"},84:{c:"T"},85:{c:"U"},86:{c:"V"},87:{c:"W"},88:{c:"X"},89:{c:"Y"},90:{c:"Z"},107:{c:"k"},988:{c:"\\E008"},1008:{c:"\\E009"},8463:{f:""},8726:{f:""},8740:{c:"\\E006"},8742:{c:"\\E007"},8808:{c:"\\E00C"},8809:{c:"\\E00D"},8816:{c:"\\E011"},8817:{c:"\\E00E"},8840:{c:"\\E016"},8841:{c:"\\E018"},8842:{c:"\\E01A"},8843:{c:"\\E01B"},10887:{c:"\\E010"},10888:{c:"\\E00F"},10955:{c:"\\E017"},10956:{c:"\\E019"}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.texVariant={32:[0,0,.25],65:[.701,0,.722],66:[.683,0,.667],67:[.702,.019,.722],68:[.683,0,.722],69:[.683,0,.667],70:[.683,0,.611],71:[.702,.019,.778],72:[.683,0,.778],73:[.683,0,.389],74:[.683,.077,.5],75:[.683,0,.778],76:[.683,0,.667],77:[.683,0,.944],78:[.683,.02,.722],79:[.701,.019,.778],80:[.683,0,.611],81:[.701,.181,.778],82:[.683,0,.722],83:[.702,.012,.556],84:[.683,0,.667],85:[.683,.019,.722],86:[.683,.02,.722],87:[.683,.019,1],88:[.683,0,.722],89:[.683,0,.722],90:[.683,0,.667],107:[.683,0,.556],160:[0,0,.25],165:[.683,0,.75],174:[.709,.175,.947],240:[.749,.021,.556],295:[.695,.013,.54],710:[.845,-.561,2.333],732:[.899,-.628,2.333],770:[.845,-.561,0],771:[.899,-.628,0],988:[.605,.085,.778],989:[.605,.085,.778],1008:[.434,.006,.667,{ic:.067}],8245:[.56,-.043,.275],8463:[.695,.013,.54],8487:[.684,.022,.722],8498:[.695,0,.556],8502:[.763,.021,.667],8503:[.764,.043,.444],8504:[.764,.043,.667],8513:[.705,.023,.639],8592:[.437,-.064,.5],8594:[.437,-.064,.5],8602:[.437,-.06,1],8603:[.437,-.06,1],8606:[.417,-.083,1],8608:[.417,-.083,1],8610:[.417,-.083,1.111],8611:[.417,-.083,1.111],8619:[.575,.041,1],8620:[.575,.041,1],8621:[.417,-.083,1.389],8622:[.437,-.06,1],8624:[.722,0,.5],8625:[.722,0,.5],8630:[.461,0,1],8631:[.46,0,1],8634:[.65,.083,.778],8635:[.65,.083,.778],8638:[.694,.194,.417],8639:[.694,.194,.417],8642:[.694,.194,.417],8643:[.694,.194,.417],8644:[.667,0,1],8646:[.667,0,1],8647:[.583,.083,1],8648:[.694,.193,.833],8649:[.583,.083,1],8650:[.694,.194,.833],8651:[.514,.014,1],8652:[.514,.014,1],8653:[.534,.035,1],8654:[.534,.037,1],8655:[.534,.035,1],8666:[.611,.111,1],8667:[.611,.111,1],8669:[.417,-.083,1],8672:[.437,-.064,1.334],8674:[.437,-.064,1.334],8705:[.846,.021,.5],8708:[.86,.166,.556],8709:[.587,0,.778],8717:[.44,0,.429],8722:[.27,-.23,.5],8724:[.766,.093,.778],8726:[.43,.023,.778],8733:[.472,-.028,.778],8736:[.694,0,.722],8737:[.714,.02,.722],8738:[.551,.051,.722],8739:[.43,.023,.222],8740:[.43,.023,.222],8741:[.431,.023,.389],8742:[.431,.024,.389],8756:[.471,.082,.667],8757:[.471,.082,.667],8764:[.365,-.132,.778],8765:[.367,-.133,.778],8769:[.467,-.032,.778],8770:[.463,-.034,.778],8774:[.652,.155,.778],8776:[.481,-.05,.778],8778:[.579,.039,.778],8782:[.492,-.008,.778],8783:[.492,-.133,.778],8785:[.609,.108,.778],8786:[.601,.101,.778],8787:[.601,.102,.778],8790:[.367,-.133,.778],8791:[.721,-.133,.778],8796:[.859,-.133,.778],8806:[.753,.175,.778],8807:[.753,.175,.778],8808:[.752,.284,.778],8809:[.752,.284,.778],8812:[.75,.25,.5],8814:[.708,.209,.778],8815:[.708,.209,.778],8816:[.919,.421,.778],8817:[.919,.421,.778],8818:[.732,.228,.778],8819:[.732,.228,.778],8822:[.681,.253,.778],8823:[.681,.253,.778],8828:[.58,.153,.778],8829:[.58,.154,.778],8830:[.732,.228,.778],8831:[.732,.228,.778],8832:[.705,.208,.778],8833:[.705,.208,.778],8840:[.828,.33,.778],8841:[.828,.33,.778],8842:[.634,.255,.778],8843:[.634,.254,.778],8847:[.539,.041,.778],8848:[.539,.041,.778],8858:[.582,.082,.778],8859:[.582,.082,.778],8861:[.582,.082,.778],8862:[.689,0,.778],8863:[.689,0,.778],8864:[.689,0,.778],8865:[.689,0,.778],8872:[.694,0,.611],8873:[.694,0,.722],8874:[.694,0,.889],8876:[.695,0,.611],8877:[.695,0,.611],8878:[.695,0,.722],8879:[.695,0,.722],8882:[.539,.041,.778],8883:[.539,.041,.778],8884:[.636,.138,.778],8885:[.636,.138,.778],8888:[.408,-.092,1.111],8890:[.431,.212,.556],8891:[.716,0,.611],8892:[.716,0,.611],8901:[.189,0,.278],8903:[.545,.044,.778],8905:[.492,-.008,.778],8906:[.492,-.008,.778],8907:[.694,.022,.778],8908:[.694,.022,.778],8909:[.464,-.036,.778],8910:[.578,.021,.76],8911:[.578,.022,.76],8912:[.54,.04,.778],8913:[.54,.04,.778],8914:[.598,.022,.667],8915:[.598,.022,.667],8916:[.736,.022,.667],8918:[.541,.041,.778],8919:[.541,.041,.778],8920:[.568,.067,1.333],8921:[.568,.067,1.333],8922:[.886,.386,.778],8923:[.886,.386,.778],8926:[.734,0,.778],8927:[.734,0,.778],8928:[.801,.303,.778],8929:[.801,.303,.778],8934:[.73,.359,.778],8935:[.73,.359,.778],8936:[.73,.359,.778],8937:[.73,.359,.778],8938:[.706,.208,.778],8939:[.706,.208,.778],8940:[.802,.303,.778],8941:[.801,.303,.778],8994:[.378,-.122,.778],8995:[.378,-.143,.778],9416:[.709,.175,.902],9484:[.694,-.306,.5],9488:[.694,-.306,.5],9492:[.366,.022,.5],9496:[.366,.022,.5],9585:[.694,.195,.889],9586:[.694,.195,.889],9632:[.689,0,.778],9633:[.689,0,.778],9650:[.575,.02,.722],9651:[.575,.02,.722],9654:[.539,.041,.778],9660:[.576,.019,.722],9661:[.576,.019,.722],9664:[.539,.041,.778],9674:[.716,.132,.667],9733:[.694,.111,.944],10003:[.706,.034,.833],10016:[.716,.022,.833],10731:[.716,.132,.667],10846:[.813,.097,.611],10877:[.636,.138,.778],10878:[.636,.138,.778],10885:[.762,.29,.778],10886:[.762,.29,.778],10887:[.801,.303,.778],10888:[.801,.303,.778],10889:[.761,.387,.778],10890:[.761,.387,.778],10891:[1.003,.463,.778],10892:[1.003,.463,.778],10901:[.636,.138,.778],10902:[.636,.138,.778],10933:[.752,.286,.778],10934:[.752,.286,.778],10935:[.761,.294,.778],10936:[.761,.294,.778],10937:[.761,.337,.778],10938:[.761,.337,.778],10949:[.753,.215,.778],10950:[.753,.215,.778],10955:[.752,.332,.778],10956:[.752,.333,.778],57350:[.43,.023,.222],57351:[.431,.024,.389],57352:[.605,.085,.778],57353:[.434,.006,.667,{ic:.067}],57356:[.752,.284,.778],57357:[.752,.284,.778],57358:[.919,.421,.778],57359:[.801,.303,.778],57360:[.801,.303,.778],57361:[.919,.421,.778],57366:[.828,.33,.778],57367:[.752,.332,.778],57368:[.828,.33,.778],57369:[.752,.333,.778],57370:[.634,.255,.778],57371:[.634,.254,.778]}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(23);e.HDW1=[.75,.25,.875],e.HDW2=[.583,.082,1],e.HDW3=[.583,.082,.5],e.VSIZES=[1,1.2,1.8,2.4,3];var i={c:47,dir:n.V,sizes:e.VSIZES},o={c:175,dir:n.H,sizes:[.59],stretch:[0,175],HDW:[.59,-.544,.5]},a={c:710,dir:n.H,sizes:[.517,.817,1.335,1.777,1.909]},s={c:732,dir:n.H,sizes:[.583,.805,1.33,1.773,1.887]},c={c:8211,dir:n.H,sizes:[.5],stretch:[0,8211],HDW:[.285,-.248,.5]},l={c:8592,dir:n.H,sizes:[1],stretch:[8592,8722],HDW:e.HDW2},u={c:8594,dir:n.H,sizes:[1],stretch:[0,8722,8594],HDW:e.HDW2},h={c:8596,dir:n.H,sizes:[1],stretch:[8592,8722,8594],HDW:e.HDW2},f={c:8612,dir:n.H,stretch:[8592,8722,8739],HDW:e.HDW3,min:1.278},p={c:8614,dir:n.H,sizes:[1],stretch:[8739,8722,8594],HDW:e.HDW2},d={c:8656,dir:n.H,sizes:[1],stretch:[8656,61],HDW:e.HDW2},m={c:8658,dir:n.H,sizes:[1],stretch:[0,61,8658],HDW:e.HDW2},y={c:8660,dir:n.H,sizes:[1],stretch:[8656,61,8658],HDW:e.HDW2},v={c:8722,dir:n.H,sizes:[.778],stretch:[0,8722],HDW:[.583,.082,.778]},b={c:8739,dir:n.V,sizes:[1],stretch:[0,8739],HDW:[.75,.249,.278]},g={c:9180,dir:n.H,sizes:[.778,1],schar:[8994,8994],stretch:[57680,57684,57681],HDW:[.32,.2,.5]},M={c:9181,dir:n.H,sizes:[.778,1],schar:[8995,8995],stretch:[57682,57684,57683],HDW:[.32,.2,.5]},O={c:9182,dir:n.H,stretch:[57680,57684,57681,57685],HDW:[.32,.2,.5],min:1.8},x={c:9183,dir:n.H,stretch:[57682,57684,57683,57686],HDW:[.32,.2,.5],min:1.8},S={c:10216,dir:n.V,sizes:e.VSIZES},E={c:10217,dir:n.V,sizes:e.VSIZES},C={c:10502,dir:n.H,stretch:[8656,61,8739],HDW:e.HDW3,min:1.278},_={c:10503,dir:n.H,stretch:[8872,61,8658],HDW:e.HDW3,min:1.278};e.delimiters={40:{dir:n.V,sizes:e.VSIZES,stretch:[9115,9116,9117],HDW:[.75,.25,.875]},41:{dir:n.V,sizes:e.VSIZES,stretch:[9118,9119,9120],HDW:[.75,.25,.875]},45:v,47:i,61:{dir:n.H,sizes:[.767],stretch:[0,61],HDW:[.583,.082,.778]},91:{dir:n.V,sizes:e.VSIZES,stretch:[9121,9122,9123],HDW:e.HDW1},92:{dir:n.V,sizes:e.VSIZES},93:{dir:n.V,sizes:e.VSIZES,stretch:[9124,9125,9126],HDW:e.HDW1},94:a,95:c,123:{dir:n.V,sizes:e.VSIZES,stretch:[9127,9130,9129,9128],HDW:[.75,.25,.889]},124:{dir:n.V,sizes:[1],stretch:[0,8739],HDW:[.75,.249,.278]},125:{dir:n.V,sizes:e.VSIZES,stretch:[9131,9130,9133,9132],HDW:[.75,.25,.889]},126:s,175:o,710:a,713:o,732:s,770:a,771:s,818:c,8211:c,8212:c,8213:c,8214:{dir:n.V,sizes:[.602,1],schar:[0,8741],stretch:[0,8741],HDW:[.75,.25,.5]},8215:c,8254:o,8407:u,8592:l,8593:{dir:n.V,sizes:[.888],stretch:[8593,9168],HDW:[.694,.193,.667]},8594:u,8595:{dir:n.V,sizes:[.888],stretch:[0,9168,8595],HDW:[.694,.194,.667]},8596:h,8597:{dir:n.V,sizes:[1.044],stretch:[8593,9168,8595],HDW:[.772,.272,.667]},8606:{dir:n.H,sizes:[1],stretch:[8606,8722],HDW:e.HDW2},8608:{dir:n.H,sizes:[1],stretch:[0,8722,8608],HDW:e.HDW2},8612:f,8613:{dir:n.V,stretch:[8593,9168,8869],HDW:e.HDW1,min:1.555},8614:p,8615:{dir:n.V,stretch:[8868,9168,8595],HDW:e.HDW1,min:1.555},8624:{dir:n.V,sizes:[.722],stretch:[8624,9168],HDW:[.722,0,.667]},8625:{dir:n.V,sizes:[.722],stretch:[8625,9168],HDW:[.722,0,.667]},8636:{dir:n.H,sizes:[1],stretch:[8636,8722],HDW:e.HDW2},8637:{dir:n.H,sizes:[1],stretch:[8637,8722],HDW:e.HDW2},8638:{dir:n.V,sizes:[.888],stretch:[8638,9168],HDW:[.694,.194,.667]},8639:{dir:n.V,sizes:[.888],stretch:[8639,9168],HDW:[.694,.194,.667]},8640:{dir:n.H,sizes:[1],stretch:[0,8722,8640],HDW:e.HDW2},8641:{dir:n.H,sizes:[1],stretch:[0,8722,8641],HDW:e.HDW2},8642:{dir:n.V,sizes:[.888],stretch:[0,9168,8642],HDW:[.694,.194,.667]},8643:{dir:n.V,sizes:[.888],stretch:[0,9168,8643],HDW:[.694,.194,.667]},8656:d,8657:{dir:n.V,sizes:[.888],stretch:[8657,8214],HDW:[.694,.194,.778]},8658:m,8659:{dir:n.V,sizes:[.888],stretch:[0,8214,8659],HDW:[.694,.194,.778]},8660:y,8661:{dir:n.V,sizes:[1.044],stretch:[8657,8214,8659],HDW:[.772,.272,.778]},8666:{dir:n.H,sizes:[1],stretch:[8666,8801],HDW:[.464,-.036,1]},8667:{dir:n.H,sizes:[1],stretch:[0,8801,8667],HDW:[.464,-.036,1]},8722:v,8725:i,8730:{dir:n.V,sizes:e.VSIZES,stretch:[57345,57344,9143],HDW:[.8,.2,1.056]},8739:b,8741:{dir:n.V,sizes:[1],stretch:[0,8741],HDW:[.75,.25,.5]},8968:{dir:n.V,sizes:e.VSIZES,stretch:[9121,9122],HDW:e.HDW1},8969:{dir:n.V,sizes:e.VSIZES,stretch:[9124,9125],HDW:e.HDW1},8970:{dir:n.V,sizes:e.VSIZES,stretch:[0,9122,9123],HDW:e.HDW1},8971:{dir:n.V,sizes:e.VSIZES,stretch:[0,9125,9126],HDW:e.HDW1},8978:g,8994:g,8995:M,9001:S,9002:E,9130:{dir:n.V,sizes:[.32],stretch:[9130,9130,9130],HDW:[.29,.015,.889]},9135:c,9136:{dir:n.V,sizes:[.989],stretch:[9127,9130,9133],HDW:[.744,.244,.889]},9137:{dir:n.V,sizes:[.989],stretch:[9131,9130,9129],HDW:[.744,.244,.889]},9140:{dir:n.H,stretch:[9484,8722,9488],HDW:e.HDW3,min:1},9141:{dir:n.H,stretch:[9492,8722,9496],HDW:e.HDW3,min:1},9168:{dir:n.V,sizes:[.602,1],schar:[0,8739],stretch:[0,8739],HDW:[.602,0,.278]},9180:g,9181:M,9182:O,9183:x,9184:{dir:n.H,stretch:[714,713,715],HDW:[.59,-.544,.5],min:1},9185:{dir:n.H,stretch:[715,713,714],HDW:[.59,-.544,.5],min:1},9472:c,10072:b,10216:S,10217:E,10222:{dir:n.V,sizes:[.989],stretch:[9127,9130,9129],HDW:[.744,.244,.889]},10223:{dir:n.V,sizes:[.989],stretch:[9131,9130,9133],HDW:[.744,.244,.889]},10229:l,10230:u,10231:h,10232:d,10233:m,10234:y,10235:f,10236:p,10237:C,10238:_,10502:C,10503:_,10574:{dir:n.H,stretch:[8636,8722,8640],HDW:e.HDW3,min:2},10575:{dir:n.V,stretch:[8638,9168,8642],HDW:e.HDW1,min:1.776},10576:{dir:n.H,stretch:[8637,8722,8641],HDW:e.HDW3,min:2},10577:{dir:n.V,stretch:[8639,9168,8643],HDW:e.HDW1,min:.5},10586:{dir:n.H,stretch:[8636,8722,8739],HDW:e.HDW3,min:1.278},10587:{dir:n.H,stretch:[8739,8722,8640],HDW:e.HDW3,min:1.278},10588:{dir:n.V,stretch:[8638,9168,8869],HDW:e.HDW1,min:1.556},10589:{dir:n.V,stretch:[8868,9168,8642],HDW:e.HDW1,min:1.556},10590:{dir:n.H,stretch:[8637,8722,8739],HDW:e.HDW3,min:1.278},10591:{dir:n.H,stretch:[8739,8722,8641],HDW:e.HDW3,min:1.278},10592:{dir:n.V,stretch:[8639,9168,8869],HDW:e.HDW1,min:1.776},10593:{dir:n.V,stretch:[8868,9168,8643],HDW:e.HDW1,min:1.776},12296:S,12297:E,65079:O,65080:x}},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),l=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")},u=this&&this.__read||function(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,i,o=r.call(t),a=[];try{for(;(void 0===e||0=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var o=r(20),l=r(13),n=r(3),i=r(225),a=r(227),s=r(228),c="undefined"!=typeof window&&window.navigator&&"Mac"===window.navigator.platform.substr(0,3),h=(Object.defineProperty(f.prototype,"isLoading",{get:function(){return 0/g,">")},f.prototype.toMML=function(t){return this.MmlVisitor.visitTree(t.root,t,{texHints:this.settings.texHints,semantics:this.settings.semantics&&"MathML"!==t.inputJax.name})},f.prototype.zoom=function(t,e,r){t&&!this.isZoomEvent(t,e)||(this.menu.mathItem=r,t&&this.menu.post(t),this.zoomBox.post())},f.prototype.isZoomEvent=function(t,e){return this.settings.zoom===e&&(!this.settings.alt||t.altKey)&&(!this.settings.ctrl||t.ctrlKey)&&(!this.settings.cmd||t.metaKey)&&(!this.settings.shift||t.shiftKey)},f.prototype.rerender=function(t){void 0===t&&(t=l.STATE.TYPESET),this.rerenderStart=Math.min(t,this.rerenderStart),f.loading||(this.document.rerender(this.rerenderStart),this.rerenderStart=l.STATE.LAST)},f.prototype.copyMathML=function(){this.copyToClipboard(this.toMML(this.menu.mathItem))},f.prototype.copyOriginal=function(){this.copyToClipboard(this.menu.mathItem.math)},f.prototype.copyAnnotation=function(){this.copyToClipboard(this.menu.annotation)},f.prototype.copyToClipboard=function(t){var e=document.createElement("textarea");e.value=t,e.setAttribute("readonly",""),e.style.cssText="height: 1px; width: 1px; padding: 1px; position: absolute; left: -10px",document.body.appendChild(e),e.select();try{document.execCommand("copy")}catch(t){alert("Can't copy to clipboard: "+t.message)}document.body.removeChild(e)},f.prototype.addMenu=function(e){var r=this,t=e.typesetRoot;t.addEventListener("contextmenu",function(){return r.menu.mathItem=e},!0),t.addEventListener("keydown",function(){return r.menu.mathItem=e},!0),t.addEventListener("click",function(t){return r.zoom(t,"Click",e)},!0),t.addEventListener("dblclick",function(t){return r.zoom(t,"DoubleClick",e)},!0),this.menu.getStore().insert(t)},f.prototype.clear=function(){this.menu.getStore().clear()},f.prototype.variable=function(e,r){var n=this;return{name:e,getter:function(){return n.settings[e]},setter:function(t){n.settings[e]=t,r&&r(t),n.saveUserSettings()}}},f.prototype.a11yVar=function(r){var n=this;return{name:r,getter:function(){return n.getA11y(r)},setter:function(t){n.settings[r]=t;var e={};e[r]=t,n.setA11y(e),n.saveUserSettings()}}},f.prototype.submenu=function(t,e,r,n){var i,o;void 0===r&&(r=[]),void 0===n&&(n=!1);var a=[];try{for(var s=u(r),c=s.next();!c.done;c=s.next()){var l=c.value;Array.isArray(l)?a=a.concat(l):a.push(l)}}catch(t){i={error:t}}finally{try{c&&!c.done&&(o=s.return)&&o.call(s)}finally{if(i)throw i.error}}return{type:"submenu",id:t,content:e,menu:{items:a},disabled:0===a.length||n}},f.prototype.command=function(t,e,r,n){return void 0===n&&(n={}),Object.assign({type:"command",id:t,content:e,action:r},n)},f.prototype.checkbox=function(t,e,r,n){return void 0===n&&(n={}),Object.assign({type:"checkbox",id:t,content:e,variable:r},n)},f.prototype.radioGroup=function(e,t){var r=this;return t.map(function(t){return r.radio(t[0],t[1]||t[0],e)})},f.prototype.radio=function(t,e,r,n){return void 0===n&&(n={}),Object.assign({type:"radio",id:t,content:e,variable:r},n)},f.prototype.label=function(t,e){return{type:"label",id:t,content:e}},f.prototype.rule=function(){return{type:"rule"}},f.MENU_STORAGE="MathJax-Menu-Settings",f.OPTIONS={settings:{texHints:!0,semantics:!1,zoom:"NoZoom",zscale:"200%",renderer:"CHTML",alt:!1,cmd:!1,ctrl:!1,shift:!1,scale:1,autocollapse:!1,collapsible:!1,inTabOrder:!0,explorer:!1},jax:{CHTML:null,SVG:null},annotationTypes:n.expandable({TeX:["TeX","LaTeX","application/x-tex"],StarMath:["StarMath 5.0"],Maple:["Maple"],ContentMathML:["MathML-Content","application/mathml-content+xml"],OpenMath:["OpenMath"]})},f.loading=0,f.loadingPromises=new Map,f._loadingPromise=null,f._loadingOK=null,f._loadingFailed=null,f);function f(t,e){var r=this;void 0===e&&(e={}),this.settings=null,this.defaultSettings=null,this.menu=null,this.MmlVisitor=new a.MmlVisitor,this.jax={CHTML:null,SVG:null},this.rerenderStart=l.STATE.LAST,this.about=new ContextMenu.Info('MathJax v'+o.mathjax.version,function(){var t=[];return t.push("Input Jax: "+r.document.inputJax.map(function(t){return t.name}).join(", ")),t.push("Output Jax: "+r.document.outputJax.name),t.push("Document Type: "+r.document.kind),t.join("
")},'www.mathjax.org'),this.help=new ContextMenu.Info("MathJax Help",function(){return["

MathJax is a JavaScript library that allows page"," authors to include mathematics within their web pages."," As a reader, you don't need to do anything to make that happen.

","

Browsers: MathJax works with all modern browsers including"," Edge, Firefox, Chrome, Safari, Opera, and most mobile browsers.

","

Math Menu: MathJax adds a contextual menu to equations."," Right-click or CTRL-click on any mathematics to access the menu.

",'
',"

Show Math As: These options allow you to view the formula's"," source markup (as MathML or in its original format).

","

Copy to Clipboard: These options copy the formula's source markup,"," as MathML or in its original format, to the clipboard"," (in browsers that support that).

","

Math Settings: These give you control over features of MathJax,"," such the size of the mathematics, and the mechanism used"," to display equations.

","

Accessibility: MathJax can work with screen"," readers to make mathematics accessible to the visually impaired."," Turn on the explorer to enable generation of speech strings"," and the ability to investigate expressions interactively.

","

Language: This menu lets you select the language used by MathJax"," for its menus and warning messages. (Not yet implemented in version 3.)

","
","

Math Zoom: If you are having difficulty reading an"," equation, MathJax can enlarge it to help you see it better, or"," you can scall all the math on the page to make it larger."," Turn these features on in the Math Settings menu.

","

Preferences: MathJax uses your browser's localStorage database"," to save the preferences set via this menu locally in your browser. These"," are not used to track you, and are not transferred or used remotely by"," MathJax in any way.

"].join("\n")},'www.mathjax.org'),this.mathmlCode=new s.SelectableInfo("MathJax MathML Expression",function(){if(!r.menu.mathItem)return"";var t=r.toMML(r.menu.mathItem);return"
"+r.formatSource(t)+"
"},""),this.originalText=new s.SelectableInfo("MathJax Original Source",function(){if(!r.menu.mathItem)return"";var t=r.menu.mathItem.math;return'
'+r.formatSource(t)+"
"},""),this.annotationText=new s.SelectableInfo("MathJax Annotation Text",function(){if(!r.menu.mathItem)return"";var t=r.menu.annotation;return'
'+r.formatSource(t)+"
"},""),this.zoomBox=new ContextMenu.Info("MathJax Zoomed Expression",function(){if(!r.menu.mathItem)return"";var t=r.menu.mathItem.typesetRoot.cloneNode(!0);return t.style.margin="0",'
'+t.outerHTML+"
"},""),this.document=t,this.options=n.userOptions(n.defaultOptions({},this.constructor.OPTIONS),e),this.initSettings(),this.mergeUserSettings(),this.initMenu()}e.Menu=h},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(96),s=r(3),c=(o=a.SerializedMmlVisitor,i(l,o),l.prototype.visitTree=function(t,e,r){return void 0===e&&(e=null),void 0===r&&(r={}),this.mathItem=e,s.userOptions(this.options,r),this.visitNode(t,"")},l.prototype.visitTeXAtomNode=function(t,e){return this.options.texHints?o.prototype.visitTeXAtomNode.call(this,t,e):t.childNodes[0]&&1===t.childNodes[0].childNodes.length?this.visitNode(t.childNodes[0],e):e+"\n"+this.childNodeMml(t,e+" ","\n")+e+""},l.prototype.visitMathNode=function(t,e){if(!this.options.semantics||"TeX"!==this.mathItem.inputJax.name)return o.prototype.visitDefault.call(this,t,e);var r=t.childNodes.length&&1\n"+e+" \n"+(r?e+" \n":"")+this.childNodeMml(t,e+(r?" ":" "),"\n")+(r?e+" \n":"")+e+' '+this.mathItem.math+"\n"+e+" \n"+e+""},l);function l(){var t=null!==o&&o.apply(this,arguments)||this;return t.options={texHints:!0,semantics:!1},t.mathItem=null,t}e.MmlVisitor=c},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0});var o,a=(o=ContextMenu.Info,i(s,o),s.prototype.addEvents=function(t){var e=this;t.addEventListener("keypress",function(t){"a"===t.key&&(t.ctrlKey||t.metaKey)&&(e.selectAll(),e.stop(t))})},s.prototype.selectAll=function(){document.getSelection().selectAllChildren(this.getHtml().querySelector("pre"))},s.prototype.copyToClipboard=function(){this.selectAll();try{document.execCommand("copy")}catch(t){alert("Can't copy to clipboard: "+t.message)}document.getSelection().removeAllRanges()},s.prototype.generateHtml=function(){var e=this;o.prototype.generateHtml.call(this);var t=this.getHtml().querySelector("span."+ContextMenu.HtmlClasses.INFOSIGNATURE).appendChild(document.createElement("input"));t.type="button",t.value="Copy to Clipboard",t.addEventListener("click",function(t){return e.copyToClipboard()})},s);function s(){return null!==o&&o.apply(this,arguments)||this}e.SelectableInfo=a},function(t,e,r){"use strict";var n,o=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),a=this&&this.__assign||function(){return(a=Object.assign||function(t){for(var e,r=1,n=arguments.length;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var l=r(20),u=r(13),h=r(3),f=r(226);function p(t){return o(e,n=t),e.prototype.addMenu=function(t){this.state()=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(S,"__esModule",{value:!0});var t,c,r,o,l,n=E(5),a=E(24);function u(t){return r.visitTree(t,c.document)}function h(){r=new S.MathJax._.core.MmlTree.SerializedMmlVisitor.SerializedMmlVisitor,o=S.MathJax._.mathjax.mathjax,c.input=v(),c.output=b(),c.adaptor=g(),c.handler&&o.handlers.unregister(c.handler),c.handler=M(),c.handler&&(o.handlers.register(c.handler),c.document=O())}function f(){var e,t;c.input&&c.output&&p();var r=c.output?c.output.name.toLowerCase():"";try{for(var n=s(c.input),i=n.next();!i.done;i=n.next()){var o=i.value,a=o.name.toLowerCase();m(a,o),y(a,o),c.output&&d(a,r,o)}}catch(t){e={error:t}}finally{try{i&&!i.done&&(t=n.return)&&t.call(n)}finally{if(e)throw e.error}}}function p(){S.MathJax.typeset=function(t){void 0===t&&(t=null),c.document.options.elements=t,c.document.render()},S.MathJax.typesetPromise=function(t){return void 0===t&&(t=null),c.document.options.elements=t,o.handleRetriesFor(function(){c.document.render()})},S.MathJax.typesetClear=function(){return c.document.clear()}}function d(t,e,r){var n=t+"2"+e;S.MathJax[n]=function(t,e){return void 0===e&&(e={}),e.format=r.name,c.document.convert(t,e)},S.MathJax[n+"Promise"]=function(t,e){return void 0===e&&(e={}),e.format=r.name,o.handleRetriesFor(function(){return c.document.convert(t,e)})},S.MathJax[e+"Stylesheet"]=function(){return c.output.styleSheet(c.document)},"getMetricsFor"in c.output&&(S.MathJax.getMetricsFor=function(t,e){return c.output.getMetricsFor(t,e)})}function m(t,r){var n=S.MathJax._.core.MathItem.STATE;S.MathJax[t+"2mml"]=function(t,e){return void 0===e&&(e={}),e.end=n.CONVERT,e.format=r.name,u(c.document.convert(t,e))},S.MathJax[t+"2mmlPromise"]=function(t,e){return void 0===e&&(e={}),e.end=n.CONVERT,e.format=r.name,o.handleRetriesFor(function(){return u(c.document.convert(t,e))})}}function y(t,e){"tex"===t&&(S.MathJax.texReset=function(t){return void 0===t&&(t=0),e.parseOptions.tags.reset(t)})}function v(){var e,t,r=[];try{for(var n=s(S.CONFIG.input),i=n.next();!i.done;i=n.next()){var o=i.value,a=c.constructors[o];if(!a)throw Error('Input Jax "'+o+'" is not defined (has it been loaded?)');r.push(new a(S.MathJax.config[o]))}}catch(t){e={error:t}}finally{try{i&&!i.done&&(t=n.return)&&t.call(n)}finally{if(e)throw e.error}}return r}function b(){var t=S.CONFIG.output;if(!t)return null;var e=c.constructors[t];if(!e)throw Error('Output Jax "'+t+'" is not defined (has it been loaded?)');return new e(S.MathJax.config[t])}function g(){var t=S.CONFIG.adaptor;if(!t||"none"===t)return null;var e=c.constructors[t];if(!e)throw Error('DOMAdaptor "'+t+'" is not defined (has it been loaded?)');return e(S.MathJax.config[t])}function M(){var e,t,r=S.CONFIG.handler;if(!r||"none"===r||!c.adaptor)return null;var n=c.constructors[r];if(!n)throw Error('Handler "'+r+'" is not defined (has it been loaded?)');var i=new n(c.adaptor,5);try{for(var o=s(l),a=o.next();!a.done;a=o.next()){i=a.value.item(i)}}catch(t){e={error:t}}finally{try{a&&!a.done&&(t=o.return)&&t.call(o)}finally{if(e)throw e.error}}return i}function O(t){return void 0===t&&(t=null),o.document(t||S.CONFIG.document,e(e({},S.MathJax.config.options),{InputJax:c.input,OutputJax:c.output}))}c=t=S.Startup||(S.Startup={}),l=new a.PrioritizedList,c.constructors={},c.input=[],c.output=null,c.handler=null,c.adaptor=null,c.elements=null,c.document=null,c.promise=new Promise(function(t,e){var r=i.document;if(r&&r.readyState&&"complete"!==r.readyState&&"interactive"!==r.readyState){var n=function(){return t()};r.defaultView.addEventListener("load",n,!0),r.defaultView.addEventListener("DOMContentLoaded",n,!0)}else t()}),c.toMML=u,c.registerConstructor=function(t,e){c.constructors[t]=e},c.useHandler=function(t,e){void 0===e&&(e=!1),S.CONFIG.handler&&!e||(S.CONFIG.handler=t)},c.useAdaptor=function(t,e){void 0===e&&(e=!1),S.CONFIG.adaptor&&!e||(S.CONFIG.adaptor=t)},c.useInput=function(t,e){void 0===e&&(e=!1),x&&!e||S.CONFIG.input.push(t)},c.useOutput=function(t,e){void 0===e&&(e=!1),S.CONFIG.output&&!e||(S.CONFIG.output=t)},c.extendHandler=function(t,e){void 0===e&&(e=10),l.add(t,e)},c.defaultReady=function(){h(),f(),c.promise=c.promise.then(function(){return S.CONFIG.pageReady()})},c.defaultPageReady=function(){return S.CONFIG.typeset&&S.MathJax.typesetPromise?S.MathJax.typesetPromise():null},c.getComponents=h,c.makeMethods=f,c.makeTypesetMethods=p,c.makeOutputMethods=d,c.makeMmlMethods=m,c.makeResetMethod=y,c.getInputJax=v,c.getOutputJax=b,c.getAdaptor=g,c.getHandler=M,c.getDocument=O,S.MathJax=n.MathJax,void 0===S.MathJax._.startup&&(n.combineDefaults(S.MathJax.config,"startup",{input:[],output:"",handler:null,adaptor:null,document:"undefined"==typeof document?"":document,elements:null,typeset:!0,ready:t.defaultReady.bind(t),pageReady:t.defaultPageReady.bind(t)}),n.combineWithMathJax({startup:t,options:{}})),S.CONFIG=S.MathJax.config.startup;var x=0!==S.CONFIG.input.length}).call(this,E(28))},function(t,e,r){"use strict";r(17).Loader.preLoad("loader","startup","core","input/tex","input/mml","output/chtml","output/chtml/fonts/tex.js","ui/menu")},function(t,e,r){"use strict";r(234);var n=r(70),i=r(81);MathJax.startup&&(MathJax.startup.registerConstructor("HTMLHandler",n.HTMLHandler),MathJax.startup.registerConstructor("browserAdaptor",i.browserAdaptor),MathJax.startup.useHandler("HTMLHandler"),MathJax.startup.useAdaptor("browserAdaptor")),MathJax.loader&&(MathJax._.mathjax.mathjax.asyncLoad=function(t){return MathJax.loader.load(t)})},function(t,e,r){"use strict";var n=r(5),i=Ct(n),o=Ct(r(79)),a=Ct(r(81)),s=Ct(r(80)),c=Ct(r(40)),l=Ct(r(82)),u=Ct(r(94)),h=Ct(r(29)),f=Ct(r(41)),p=Ct(r(13)),d=Ct(r(43)),m=Ct(r(19)),y=Ct(r(85)),v=Ct(r(235)),b=Ct(r(44)),g=Ct(r(0)),M=Ct(r(67)),O=Ct(r(59)),x=Ct(r(90)),S=Ct(r(91)),E=Ct(r(46)),C=Ct(r(92)),_=Ct(r(58)),T=Ct(r(88)),w=Ct(r(57)),A=Ct(r(53)),k=Ct(r(65)),I=Ct(r(47)),L=Ct(r(61)),N=Ct(r(48)),P=Ct(r(26)),B=Ct(r(56)),R=Ct(r(89)),j=Ct(r(55)),H=Ct(r(52)),D=Ct(r(51)),X=Ct(r(50)),F=Ct(r(54)),W=Ct(r(87)),J=Ct(r(31)),q=Ct(r(62)),V=Ct(r(64)),U=Ct(r(49)),z=Ct(r(63)),G=Ct(r(60)),K=Ct(r(66)),Z=Ct(r(68)),Y=Ct(r(86)),$=Ct(r(96)),Q=Ct(r(42)),tt=Ct(r(30)),et=Ct(r(45)),rt=Ct(r(84)),nt=Ct(r(95)),it=Ct(r(97)),ot=Ct(r(98)),at=Ct(r(236)),st=Ct(r(99)),ct=Ct(r(102)),lt=Ct(r(70)),ut=Ct(r(100)),ht=Ct(r(101)),ft=Ct(r(20)),pt=Ct(r(103)),dt=Ct(r(93)),mt=Ct(r(12)),yt=Ct(r(25)),vt=Ct(r(83)),bt=Ct(r(3)),gt=Ct(r(24)),Mt=Ct(r(69)),Ot=Ct(r(71)),xt=Ct(r(14)),St=Ct(r(104)),Et=Ct(r(10));function Ct(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e.default=t,e}(0,n.combineWithMathJax)({_:{adaptors:{HTMLAdaptor:o,browserAdaptor:a},components:{global:i},core:{DOMAdaptor:s,FindMath:c,Handler:l,HandlerList:u,InputJax:h,MathDocument:f,MathItem:p,MathList:d,MmlTree:{Attributes:m,MML:y,MathMLVisitor:v,MmlFactory:b,MmlNode:g,MmlNodes:{TeXAtom:M,maction:O,maligngroup:x,malignmark:S,math:E,mathchoice:C,menclose:_,merror:T,mfenced:w,mfrac:A,mglyph:k,mi:I,mmultiscripts:L,mn:N,mo:P,mpadded:B,mphantom:R,mroot:j,mrow:H,ms:D,mspace:X,msqrt:F,mstyle:W,msubsup:J,mtable:q,mtd:V,mtext:U,mtr:z,munderover:G,semantics:K},MmlVisitor:Z,OperatorDictionary:Y,SerializedMmlVisitor:$},OutputJax:Q,Tree:{Factory:tt,Node:et,NodeFactory:rt,Visitor:nt,Wrapper:it,WrapperFactory:ot}},handlers:{html_ts:at,html:{HTMLDocument:st,HTMLDomStrings:ct,HTMLHandler:lt,HTMLMathItem:ut,HTMLMathList:ht}},mathjax:ft,util:{AsyncLoad:pt,BitField:dt,Entities:mt,FunctionList:yt,LinkedList:vt,Options:bt,PrioritizedList:gt,Retries:Mt,Styles:Ot,lengths:xt,numeric:St,string:Et}}})},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),l=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(68),s=(o=a.MmlVisitor,i(c,o),c.prototype.visitTree=function(t,e){var r=(this.document=e).createElement("top");return this.visitNode(t,r),this.document=null,r.firstChild},c.prototype.visitTextNode=function(t,e){e.appendChild(this.document.createTextNode(t.getText()))},c.prototype.visitXMLNode=function(t,e){e.appendChild(t.getXML().cloneNode(!0))},c.prototype.visitInferredMrowNode=function(t,e){var r,n;try{for(var i=l(t.childNodes),o=i.next();!o.done;o=i.next()){var a=o.value;this.visitNode(a,e)}}catch(t){r={error:t}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}},c.prototype.visitDefault=function(t,e){var r,n,i=this.document.createElement(t.kind);this.addAttributes(t,i);try{for(var o=l(t.childNodes),a=o.next();!a.done;a=o.next()){var s=a.value;this.visitNode(s,i)}}catch(t){r={error:t}}finally{try{a&&!a.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}e.appendChild(i)},c.prototype.addAttributes=function(t,e){var r,n,i=t.attributes,o=i.getExplicitNames();try{for(var a=l(o),s=a.next();!s.done;s=a.next()){var c=s.value;e.setAttribute(c,i.getExplicit(c).toString())}}catch(t){r={error:t}}finally{try{s&&!s.done&&(n=a.return)&&n.call(a)}finally{if(r)throw r.error}}},c);function c(){var t=null!==o&&o.apply(this,arguments)||this;return t.document=null,t}e.MathMLVisitor=s},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(20),i=r(70);e.RegisterHTMLHandler=function(t){var e=new i.HTMLHandler(t);return n.mathjax.handlers.register(e),e}},function(t,e,r){"use strict";r(238);var n=r(249);r(17).Loader.preLoad("input/tex-base","[tex]/ams","[tex]/newcommand","[tex]/noundefined","[tex]/require","[tex]/autoload","[tex]/configMacros"),(0,n.registerTeX)(["base","ams","newcommand","noundefined","require","autoload","configMacros"])},function(t,e,r){"use strict";var n=r(5),i=j(r(105)),o=j(r(11)),a=j(r(107)),s=j(r(106)),c=j(r(8)),l=j(r(112)),u=j(r(6)),h=j(r(33)),f=j(r(110)),p=j(r(7)),d=j(r(109)),m=j(r(32)),y=j(r(111)),v=j(r(22)),b=j(r(9)),g=j(r(27)),M=j(r(15)),O=j(r(4)),x=j(r(21)),S=j(r(242)),E=j(r(114)),C=j(r(115)),_=j(r(244)),T=j(r(113)),w=j(r(34)),A=j(r(35)),k=j(r(245)),I=j(r(246)),L=j(r(118)),N=j(r(72)),P=j(r(117)),B=j(r(248)),R=j(r(116));function j(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e.default=t,e}(0,n.combineWithMathJax)({_:{input:{tex_ts:i,tex:{Configuration:o,FilterUtil:a,FindTeX:s,MapHandler:c,NodeFactory:l,NodeUtil:u,ParseMethods:h,ParseOptions:f,ParseUtil:p,Stack:d,StackItem:m,StackItemFactory:y,Symbol:v,SymbolMap:b,Tags:g,TexConstants:M,TexError:O,TexParser:x,ams:{AmsConfiguration:S,AmsItems:E,AmsMethods:C},autoload:{AutoloadConfiguration:_},base:{BaseConfiguration:T,BaseItems:w,BaseMethods:A},config_macros:{ConfigMacrosConfiguration:k},newcommand:{NewcommandConfiguration:I,NewcommandItems:L,NewcommandMethods:N,NewcommandUtil:P},noundefined:{NoUndefinedConfiguration:B},require:{RequireConfiguration:R}}}}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(9),i=r(15),o=r(35),a=r(33),s=r(0);new n.RegExpMap("letter",a.default.variable,/[a-z]/i),new n.RegExpMap("digit",a.default.digit,/[0-9.,]/),new n.RegExpMap("command",a.default.controlSequence,/^\\/),new n.MacroMap("special",{"{":"Open","}":"Close","~":"Tilde","^":"Superscript",_:"Subscript"," ":"Space","\t":"Space","\r":"Space","\n":"Space","'":"Prime","%":"Comment","&":"Entry","#":"Hash","\xa0":"Space","\u2019":"Prime"},o.default),new n.CharacterMap("mathchar0mi",a.default.mathchar0mi,{alpha:"\u03b1",beta:"\u03b2",gamma:"\u03b3",delta:"\u03b4",epsilon:"\u03f5",zeta:"\u03b6",eta:"\u03b7",theta:"\u03b8",iota:"\u03b9",kappa:"\u03ba",lambda:"\u03bb",mu:"\u03bc",nu:"\u03bd",xi:"\u03be",omicron:"\u03bf",pi:"\u03c0",rho:"\u03c1",sigma:"\u03c3",tau:"\u03c4",upsilon:"\u03c5",phi:"\u03d5",chi:"\u03c7",psi:"\u03c8",omega:"\u03c9",varepsilon:"\u03b5",vartheta:"\u03d1",varpi:"\u03d6",varrho:"\u03f1",varsigma:"\u03c2",varphi:"\u03c6",S:["\xa7",{mathvariant:i.TexConstant.Variant.NORMAL}],aleph:["\u2135",{mathvariant:i.TexConstant.Variant.NORMAL}],hbar:["\u210f",{variantForm:!0}],imath:"\u0131",jmath:"\u0237",ell:"\u2113",wp:["\u2118",{mathvariant:i.TexConstant.Variant.NORMAL}],Re:["\u211c",{mathvariant:i.TexConstant.Variant.NORMAL}],Im:["\u2111",{mathvariant:i.TexConstant.Variant.NORMAL}],partial:["\u2202",{mathvariant:i.TexConstant.Variant.NORMAL}],infty:["\u221e",{mathvariant:i.TexConstant.Variant.NORMAL}],prime:["\u2032",{mathvariant:i.TexConstant.Variant.NORMAL,variantForm:!0}],emptyset:["\u2205",{mathvariant:i.TexConstant.Variant.NORMAL}],nabla:["\u2207",{mathvariant:i.TexConstant.Variant.NORMAL}],top:["\u22a4",{mathvariant:i.TexConstant.Variant.NORMAL}],bot:["\u22a5",{mathvariant:i.TexConstant.Variant.NORMAL}],angle:["\u2220",{mathvariant:i.TexConstant.Variant.NORMAL}],triangle:["\u25b3",{mathvariant:i.TexConstant.Variant.NORMAL}],backslash:["\u2216",{mathvariant:i.TexConstant.Variant.NORMAL,variantForm:!0}],forall:["\u2200",{mathvariant:i.TexConstant.Variant.NORMAL}],exists:["\u2203",{mathvariant:i.TexConstant.Variant.NORMAL}],neg:["\xac",{mathvariant:i.TexConstant.Variant.NORMAL}],lnot:["\xac",{mathvariant:i.TexConstant.Variant.NORMAL}],flat:["\u266d",{mathvariant:i.TexConstant.Variant.NORMAL}],natural:["\u266e",{mathvariant:i.TexConstant.Variant.NORMAL}],sharp:["\u266f",{mathvariant:i.TexConstant.Variant.NORMAL}],clubsuit:["\u2663",{mathvariant:i.TexConstant.Variant.NORMAL}],diamondsuit:["\u2662",{mathvariant:i.TexConstant.Variant.NORMAL}],heartsuit:["\u2661",{mathvariant:i.TexConstant.Variant.NORMAL}],spadesuit:["\u2660",{mathvariant:i.TexConstant.Variant.NORMAL}]}),new n.CharacterMap("mathchar0mo",a.default.mathchar0mo,{surd:"\u221a",coprod:["\u2210",{texClass:s.TEXCLASS.OP,movesupsub:!0}],bigvee:["\u22c1",{texClass:s.TEXCLASS.OP,movesupsub:!0}],bigwedge:["\u22c0",{texClass:s.TEXCLASS.OP,movesupsub:!0}],biguplus:["\u2a04",{texClass:s.TEXCLASS.OP,movesupsub:!0}],bigcap:["\u22c2",{texClass:s.TEXCLASS.OP,movesupsub:!0}],bigcup:["\u22c3",{texClass:s.TEXCLASS.OP,movesupsub:!0}],int:["\u222b",{texClass:s.TEXCLASS.OP}],intop:["\u222b",{texClass:s.TEXCLASS.OP,movesupsub:!0,movablelimits:!0}],iint:["\u222c",{texClass:s.TEXCLASS.OP}],iiint:["\u222d",{texClass:s.TEXCLASS.OP}],prod:["\u220f",{texClass:s.TEXCLASS.OP,movesupsub:!0}],sum:["\u2211",{texClass:s.TEXCLASS.OP,movesupsub:!0}],bigotimes:["\u2a02",{texClass:s.TEXCLASS.OP,movesupsub:!0}],bigoplus:["\u2a01",{texClass:s.TEXCLASS.OP,movesupsub:!0}],bigodot:["\u2a00",{texClass:s.TEXCLASS.OP,movesupsub:!0}],oint:["\u222e",{texClass:s.TEXCLASS.OP}],bigsqcup:["\u2a06",{texClass:s.TEXCLASS.OP,movesupsub:!0}],smallint:["\u222b",{largeop:!1}],triangleleft:"\u25c3",triangleright:"\u25b9",bigtriangleup:"\u25b3",bigtriangledown:"\u25bd",wedge:"\u2227",land:"\u2227",vee:"\u2228",lor:"\u2228",cap:"\u2229",cup:"\u222a",ddagger:"\u2021",dagger:"\u2020",sqcap:"\u2293",sqcup:"\u2294",uplus:"\u228e",amalg:"\u2a3f",diamond:"\u22c4",bullet:"\u2219",wr:"\u2240",div:"\xf7",odot:["\u2299",{largeop:!1}],oslash:["\u2298",{largeop:!1}],otimes:["\u2297",{largeop:!1}],ominus:["\u2296",{largeop:!1}],oplus:["\u2295",{largeop:!1}],mp:"\u2213",pm:"\xb1",circ:"\u2218",bigcirc:"\u25ef",setminus:["\u2216",{variantForm:!0}],cdot:"\u22c5",ast:"\u2217",times:"\xd7",star:"\u22c6",propto:"\u221d",sqsubseteq:"\u2291",sqsupseteq:"\u2292",parallel:"\u2225",mid:"\u2223",dashv:"\u22a3",vdash:"\u22a2",leq:"\u2264",le:"\u2264",geq:"\u2265",ge:"\u2265",lt:"<",gt:">",succ:"\u227b",prec:"\u227a",approx:"\u2248",succeq:"\u2ab0",preceq:"\u2aaf",supset:"\u2283",subset:"\u2282",supseteq:"\u2287",subseteq:"\u2286",in:"\u2208",ni:"\u220b",notin:"\u2209",owns:"\u220b",gg:"\u226b",ll:"\u226a",sim:"\u223c",simeq:"\u2243",perp:"\u22a5",equiv:"\u2261",asymp:"\u224d",smile:"\u2323",frown:"\u2322",ne:"\u2260",neq:"\u2260",cong:"\u2245",doteq:"\u2250",bowtie:"\u22c8",models:"\u22a8",notChar:"\u29f8",Leftrightarrow:"\u21d4",Leftarrow:"\u21d0",Rightarrow:"\u21d2",leftrightarrow:"\u2194",leftarrow:"\u2190",gets:"\u2190",rightarrow:"\u2192",to:"\u2192",mapsto:"\u21a6",leftharpoonup:"\u21bc",leftharpoondown:"\u21bd",rightharpoonup:"\u21c0",rightharpoondown:"\u21c1",nearrow:"\u2197",searrow:"\u2198",nwarrow:"\u2196",swarrow:"\u2199",rightleftharpoons:"\u21cc",hookrightarrow:"\u21aa",hookleftarrow:"\u21a9",longleftarrow:"\u27f5",Longleftarrow:"\u27f8",longrightarrow:"\u27f6",Longrightarrow:"\u27f9",Longleftrightarrow:"\u27fa",longleftrightarrow:"\u27f7",longmapsto:"\u27fc",ldots:"\u2026",cdots:"\u22ef",vdots:"\u22ee",ddots:"\u22f1",dotsc:"\u2026",dotsb:"\u22ef",dotsm:"\u22ef",dotsi:"\u22ef",dotso:"\u2026",ldotp:[".",{texClass:s.TEXCLASS.PUNCT}],cdotp:["\u22c5",{texClass:s.TEXCLASS.PUNCT}],colon:[":",{texClass:s.TEXCLASS.PUNCT}]}),new n.CharacterMap("mathchar7",a.default.mathchar7,{Gamma:"\u0393",Delta:"\u0394",Theta:"\u0398",Lambda:"\u039b",Xi:"\u039e",Pi:"\u03a0",Sigma:"\u03a3",Upsilon:"\u03a5",Phi:"\u03a6",Psi:"\u03a8",Omega:"\u03a9",_:"_","#":"#",$:"$","%":"%","&":"&",And:"&"}),new n.DelimiterMap("delimiter",a.default.delimiter,{"(":"(",")":")","[":"[","]":"]","<":"\u27e8",">":"\u27e9","\\lt":"\u27e8","\\gt":"\u27e9","/":"/","|":["|",{texClass:s.TEXCLASS.ORD}],".":"","\\\\":"\\","\\lmoustache":"\u23b0","\\rmoustache":"\u23b1","\\lgroup":"\u27ee","\\rgroup":"\u27ef","\\arrowvert":"\u23d0","\\Arrowvert":"\u2016","\\bracevert":"\u23aa","\\Vert":["\u2225",{texClass:s.TEXCLASS.ORD}],"\\|":["\u2225",{texClass:s.TEXCLASS.ORD}],"\\vert":["|",{texClass:s.TEXCLASS.ORD}],"\\uparrow":"\u2191","\\downarrow":"\u2193","\\updownarrow":"\u2195","\\Uparrow":"\u21d1","\\Downarrow":"\u21d3","\\Updownarrow":"\u21d5","\\backslash":"\\","\\rangle":"\u27e9","\\langle":"\u27e8","\\rbrace":"}","\\lbrace":"{","\\}":"}","\\{":"{","\\rceil":"\u2309","\\lceil":"\u2308","\\rfloor":"\u230b","\\lfloor":"\u230a","\\lbrack":"[","\\rbrack":"]"}),new n.CommandMap("macros",{displaystyle:["SetStyle","D",!0,0],textstyle:["SetStyle","T",!1,0],scriptstyle:["SetStyle","S",!1,1],scriptscriptstyle:["SetStyle","SS",!1,2],rm:["SetFont",i.TexConstant.Variant.NORMAL],mit:["SetFont",i.TexConstant.Variant.ITALIC],oldstyle:["SetFont",i.TexConstant.Variant.OLDSTYLE],cal:["SetFont",i.TexConstant.Variant.CALLIGRAPHIC],it:["SetFont","-tex-mathit"],bf:["SetFont",i.TexConstant.Variant.BOLD],bbFont:["SetFont",i.TexConstant.Variant.DOUBLESTRUCK],scr:["SetFont",i.TexConstant.Variant.SCRIPT],frak:["SetFont",i.TexConstant.Variant.FRAKTUR],sf:["SetFont",i.TexConstant.Variant.SANSSERIF],tt:["SetFont",i.TexConstant.Variant.MONOSPACE],tiny:["SetSize",.5],Tiny:["SetSize",.6],scriptsize:["SetSize",.7],small:["SetSize",.85],normalsize:["SetSize",1],large:["SetSize",1.2],Large:["SetSize",1.44],LARGE:["SetSize",1.73],huge:["SetSize",2.07],Huge:["SetSize",2.49],arcsin:["NamedFn"],arccos:["NamedFn"],arctan:["NamedFn"],arg:["NamedFn"],cos:["NamedFn"],cosh:["NamedFn"],cot:["NamedFn"],coth:["NamedFn"],csc:["NamedFn"],deg:["NamedFn"],det:"NamedOp",dim:["NamedFn"],exp:["NamedFn"],gcd:"NamedOp",hom:["NamedFn"],inf:"NamedOp",ker:["NamedFn"],lg:["NamedFn"],lim:"NamedOp",liminf:["NamedOp","lim inf"],limsup:["NamedOp","lim sup"],ln:["NamedFn"],log:["NamedFn"],max:"NamedOp",min:"NamedOp",Pr:"NamedOp",sec:["NamedFn"],sin:["NamedFn"],sinh:["NamedFn"],sup:"NamedOp",tan:["NamedFn"],tanh:["NamedFn"],limits:["Limits",1],nolimits:["Limits",0],overline:["UnderOver","00AF",null,1],underline:["UnderOver","005F"],overbrace:["UnderOver","23DE",1],underbrace:["UnderOver","23DF",1],overparen:["UnderOver","23DC"],underparen:["UnderOver","23DD"],overrightarrow:["UnderOver","2192"],underrightarrow:["UnderOver","2192"],overleftarrow:["UnderOver","2190"],underleftarrow:["UnderOver","2190"],overleftrightarrow:["UnderOver","2194"],underleftrightarrow:["UnderOver","2194"],overset:"Overset",underset:"Underset",stackrel:["Macro","\\mathrel{\\mathop{#2}\\limits^{#1}}",2],over:"Over",overwithdelims:"Over",atop:"Over",atopwithdelims:"Over",above:"Over",abovewithdelims:"Over",brace:["Over","{","}"],brack:["Over","[","]"],choose:["Over","(",")"],frac:"Frac",sqrt:"Sqrt",root:"Root",uproot:["MoveRoot","upRoot"],leftroot:["MoveRoot","leftRoot"],left:"LeftRight",right:"LeftRight",middle:"Middle",llap:"Lap",rlap:"Lap",raise:"RaiseLower",lower:"RaiseLower",moveleft:"MoveLeftRight",moveright:"MoveLeftRight",",":["Spacer",i.TexConstant.Length.THINMATHSPACE],":":["Spacer",i.TexConstant.Length.MEDIUMMATHSPACE],">":["Spacer",i.TexConstant.Length.MEDIUMMATHSPACE],";":["Spacer",i.TexConstant.Length.THICKMATHSPACE],"!":["Spacer",i.TexConstant.Length.NEGATIVETHINMATHSPACE],enspace:["Spacer",".5em"],quad:["Spacer","1em"],qquad:["Spacer","2em"],thinspace:["Spacer",i.TexConstant.Length.THINMATHSPACE],negthinspace:["Spacer",i.TexConstant.Length.NEGATIVETHINMATHSPACE],hskip:"Hskip",hspace:"Hskip",kern:"Hskip",mskip:"Hskip",mspace:"Hskip",mkern:"Hskip",rule:"rule",Rule:["Rule"],Space:["Rule","blank"],big:["MakeBig",s.TEXCLASS.ORD,.85],Big:["MakeBig",s.TEXCLASS.ORD,1.15],bigg:["MakeBig",s.TEXCLASS.ORD,1.45],Bigg:["MakeBig",s.TEXCLASS.ORD,1.75],bigl:["MakeBig",s.TEXCLASS.OPEN,.85],Bigl:["MakeBig",s.TEXCLASS.OPEN,1.15],biggl:["MakeBig",s.TEXCLASS.OPEN,1.45],Biggl:["MakeBig",s.TEXCLASS.OPEN,1.75],bigr:["MakeBig",s.TEXCLASS.CLOSE,.85],Bigr:["MakeBig",s.TEXCLASS.CLOSE,1.15],biggr:["MakeBig",s.TEXCLASS.CLOSE,1.45],Biggr:["MakeBig",s.TEXCLASS.CLOSE,1.75],bigm:["MakeBig",s.TEXCLASS.REL,.85],Bigm:["MakeBig",s.TEXCLASS.REL,1.15],biggm:["MakeBig",s.TEXCLASS.REL,1.45],Biggm:["MakeBig",s.TEXCLASS.REL,1.75],mathord:["TeXAtom",s.TEXCLASS.ORD],mathop:["TeXAtom",s.TEXCLASS.OP],mathopen:["TeXAtom",s.TEXCLASS.OPEN],mathclose:["TeXAtom",s.TEXCLASS.CLOSE],mathbin:["TeXAtom",s.TEXCLASS.BIN],mathrel:["TeXAtom",s.TEXCLASS.REL],mathpunct:["TeXAtom",s.TEXCLASS.PUNCT],mathinner:["TeXAtom",s.TEXCLASS.INNER],vcenter:["TeXAtom",s.TEXCLASS.VCENTER],buildrel:"BuildRel",hbox:["HBox",0],text:"HBox",mbox:["HBox",0],fbox:"FBox",strut:"Strut",mathstrut:["Macro","\\vphantom{(}"],phantom:"Phantom",vphantom:["Phantom",1,0],hphantom:["Phantom",0,1],smash:"Smash",acute:["Accent","00B4"],grave:["Accent","0060"],ddot:["Accent","00A8"],tilde:["Accent","007E"],bar:["Accent","00AF"],breve:["Accent","02D8"],check:["Accent","02C7"],hat:["Accent","005E"],vec:["Accent","2192"],dot:["Accent","02D9"],widetilde:["Accent","007E",1],widehat:["Accent","005E",1],matrix:"Matrix",array:"Matrix",pmatrix:["Matrix","(",")"],cases:["Matrix","{","","left left",null,".1em",null,!0],eqalign:["Matrix",null,null,"right left",i.TexConstant.Length.THICKMATHSPACE,".5em","D"],displaylines:["Matrix",null,null,"center",null,".5em","D"],cr:"Cr","\\":"CrLaTeX",newline:"Cr",hline:["HLine","solid"],hdashline:["HLine","dashed"],eqalignno:["Matrix",null,null,"right left",i.TexConstant.Length.THICKMATHSPACE,".5em","D",null,"right"],leqalignno:["Matrix",null,null,"right left",i.TexConstant.Length.THICKMATHSPACE,".5em","D",null,"left"],hfill:"HFill",hfil:"HFill",hfilll:"HFill",bmod:["Macro",'\\mmlToken{mo}[lspace="thickmathspace" rspace="thickmathspace"]{mod}'],pmod:["Macro","\\pod{\\mmlToken{mi}{mod}\\kern 6mu #1}",1],mod:["Macro","\\mathchoice{\\kern18mu}{\\kern12mu}{\\kern12mu}{\\kern12mu}\\mmlToken{mi}{mod}\\,\\,#1",1],pod:["Macro","\\mathchoice{\\kern18mu}{\\kern8mu}{\\kern8mu}{\\kern8mu}(#1)",1],iff:["Macro","\\;\\Longleftrightarrow\\;"],skew:["Macro","{{#2{#3\\mkern#1mu}\\mkern-#1mu}{}}",3],mathcal:["Macro","{\\cal #1}",1],mathscr:["Macro","{\\scr #1}",1],mathrm:["Macro","{\\rm #1}",1],mathbf:["Macro","{\\bf #1}",1],mathbb:["Macro","{\\bbFont #1}",1],Bbb:["Macro","{\\bbFont #1}",1],mathit:["Macro","{\\it #1}",1],mathfrak:["Macro","{\\frak #1}",1],mathsf:["Macro","{\\sf #1}",1],mathtt:["Macro","{\\tt #1}",1],textrm:["Macro","\\mathord{\\rm\\text{#1}}",1],textit:["Macro","\\mathord{\\it\\text{#1}}",1],textbf:["Macro","\\mathord{\\bf\\text{#1}}",1],textsf:["Macro","\\mathord{\\sf\\text{#1}}",1],texttt:["Macro","\\mathord{\\tt\\text{#1}}",1],pmb:["Macro","\\rlap{#1}\\kern1px{#1}",1],TeX:["Macro","T\\kern-.14em\\lower.5ex{E}\\kern-.115em X"],LaTeX:["Macro","L\\kern-.325em\\raise.21em{\\scriptstyle{A}}\\kern-.17em\\TeX"]," ":["Macro","\\text{ }"],not:"Not",dots:"Dots",space:"Tilde","\xa0":"Tilde",begin:"BeginEnd",end:"BeginEnd",label:"HandleLabel",ref:"HandleRef",nonumber:"HandleNoTag",mathchoice:"MathChoice",mmlToken:"MmlToken"},o.default);new n.EnvironmentMap("environment",a.default.environment,{array:["AlignedArray"],equation:["Equation",null,!0],"equation*":["Equation",null,!1],eqnarray:["EqnArray",null,!0,!0,"rcl","0 "+i.TexConstant.Length.THICKMATHSPACE,".5em"]},o.default);new n.CharacterMap("not_remap",null,{"\u2190":"\u219a","\u2192":"\u219b","\u2194":"\u21ae","\u21d0":"\u21cd","\u21d2":"\u21cf","\u21d4":"\u21ce","\u2208":"\u2209","\u220b":"\u220c","\u2223":"\u2224","\u2225":"\u2226","\u223c":"\u2241","~":"\u2241","\u2243":"\u2244","\u2245":"\u2247","\u2248":"\u2249","\u224d":"\u226d","=":"\u2260","\u2261":"\u2262","<":"\u226e",">":"\u226f","\u2264":"\u2270","\u2265":"\u2271","\u2272":"\u2274","\u2273":"\u2275","\u2276":"\u2278","\u2277":"\u2279","\u227a":"\u2280","\u227b":"\u2281","\u2282":"\u2284","\u2283":"\u2285","\u2286":"\u2288","\u2287":"\u2289","\u22a2":"\u22ac","\u22a8":"\u22ad","\u22a9":"\u22ae","\u22ab":"\u22af","\u227c":"\u22e0","\u227d":"\u22e1","\u2291":"\u22e2","\u2292":"\u22e3","\u22b2":"\u22ea","\u22b3":"\u22eb","\u22b4":"\u22ec","\u22b5":"\u22ed","\u2203":"\u2204"})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),r(12).add({Pcy:"\u041f",Poincareplane:"\u210c",Pr:"\u2abb",Prime:"\u2033",Proportion:"\u2237",par:"\u2225",para:"\xb6",parallel:"\u2225",parsim:"\u2af3",parsl:"\u2afd",part:"\u2202",pcy:"\u043f",percnt:"%",permil:"\u2030",perp:"\u22a5",pertenk:"\u2031",phmmat:"\u2133",phone:"\u260e",pitchfork:"\u22d4",planck:"\u210f",planckh:"\u210e",plankv:"\u210f",plus:"+",plusacir:"\u2a23",plusb:"\u229e",pluscir:"\u2a22",plusdo:"\u2214",plusdu:"\u2a25",pluse:"\u2a72",plusmn:"\xb1",plussim:"\u2a26",plustwo:"\u2a27",pm:"\xb1",pointint:"\u2a15",pound:"\xa3",pr:"\u227a",prE:"\u2ab3",prcue:"\u227c",pre:"\u2aaf",prec:"\u227a",precapprox:"\u2ab7",preccurlyeq:"\u227c",preceq:"\u2aaf",precsim:"\u227e",primes:"\u2119",prnE:"\u2ab5",prnap:"\u2ab9",prnsim:"\u22e8",prod:"\u220f",profalar:"\u232e",profline:"\u2312",profsurf:"\u2313",prop:"\u221d",propto:"\u221d",prsim:"\u227e",prurel:"\u22b0",puncsp:"\u2008"},"p")},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),r(12).add({RBarr:"\u2910",REG:"\xae",Racute:"\u0154",Rang:"\u27eb",Rarrtl:"\u2916",Rcaron:"\u0158",Rcedil:"\u0156",Rcy:"\u0420",ReverseElement:"\u220b",ReverseUpEquilibrium:"\u296f",Rho:"\u03a1",RightArrowBar:"\u21e5",RightDoubleBracket:"\u27e7",RightDownTeeVector:"\u295d",RightDownVectorBar:"\u2955",RightTeeVector:"\u295b",RightTriangleBar:"\u29d0",RightUpDownVector:"\u294f",RightUpTeeVector:"\u295c",RightUpVectorBar:"\u2954",RightVectorBar:"\u2953",RoundImplies:"\u2970",RuleDelayed:"\u29f4",rAarr:"\u21db",rArr:"\u21d2",rAtail:"\u291c",rBarr:"\u290f",rHar:"\u2964",race:"\u223d\u0331",racute:"\u0155",radic:"\u221a",raemptyv:"\u29b3",rang:"\u27e9",rangd:"\u2992",range:"\u29a5",rangle:"\u27e9",raquo:"\xbb",rarr:"\u2192",rarrap:"\u2975",rarrb:"\u21e5",rarrbfs:"\u2920",rarrc:"\u2933",rarrfs:"\u291e",rarrhk:"\u21aa",rarrlp:"\u21ac",rarrpl:"\u2945",rarrsim:"\u2974",rarrw:"\u219d",ratail:"\u291a",ratio:"\u2236",rationals:"\u211a",rbarr:"\u290d",rbbrk:"\u2773",rbrke:"\u298c",rbrksld:"\u298e",rbrkslu:"\u2990",rcaron:"\u0159",rcedil:"\u0157",rceil:"\u2309",rcub:"}",rcy:"\u0440",rdca:"\u2937",rdldhar:"\u2969",rdquo:"\u201d",rdquor:"\u201d",rdsh:"\u21b3",real:"\u211c",realine:"\u211b",realpart:"\u211c",reals:"\u211d",rect:"\u25ad",reg:"\xae",rfisht:"\u297d",rfloor:"\u230b",rhard:"\u21c1",rharu:"\u21c0",rharul:"\u296c",rightarrow:"\u2192",rightarrowtail:"\u21a3",rightharpoondown:"\u21c1",rightharpoonup:"\u21c0",rightleftarrows:"\u21c4",rightleftharpoons:"\u21cc",rightsquigarrow:"\u219d",risingdotseq:"\u2253",rlarr:"\u21c4",rlhar:"\u21cc",rlm:"\u200f",rmoustache:"\u23b1",rnmid:"\u2aee",roang:"\u27ed",roarr:"\u21fe",robrk:"\u27e7",ropar:"\u2986",roplus:"\u2a2e",rotimes:"\u2a35",rpar:")",rpargt:"\u2994",rppolint:"\u2a12",rrarr:"\u21c9",rsaquo:"\u203a",rsh:"\u21b1",rsqb:"]",rsquo:"\u2019",rsquor:"\u2019",rthree:"\u22cc",rtrie:"\u22b5",rtrif:"\u25b8",rtriltri:"\u29ce",ruluhar:"\u2968",rx:"\u211e"},"r")},function(t,e,r){"use strict";var n,i,o=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0});var a=r(11),s=r(114),c=r(27);r(243);var l,u=(l=c.AbstractTags,o(h,l),h);function h(){return null!==l&&l.apply(this,arguments)||this}e.AmsTags=u;e.AmsConfiguration=a.Configuration.create("ams",{handler:{delimiter:["AMSsymbols-delimiter","AMSmath-delimiter"],macro:["AMSsymbols-mathchar0mi","AMSsymbols-mathchar0m0","AMSsymbols-delimiter","AMSsymbols-macros","AMSmath-mathchar0mo","AMSmath-macros","AMSmath-delimiter"],environment:["AMSmath-environment"]},items:(i={},i[s.MultlineItem.prototype.kind]=s.MultlineItem,i),tags:{ams:u},init:function(t){t.append(a.Configuration.extension())}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});function n(t){for(var e=[],r=0,n=t.length;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var o,a=r(11),s=r(9),T=r(22),w=r(116),b=r(18),c=r(3),l=(o=s.CommandMap,i(u,o),u.prototype.remove=function(t){this.map.delete(t)},u);function u(){return null!==o&&o.apply(this,arguments)||this}function A(t,e,r,n){var i,o,a,s;if(b.Package.packages.has(t.options.require.prefix+r)){var c=t.options.autoload[r],l=C(2===c.length&&Array.isArray(c[0])?c:[c,[]],2),u=l[0],h=l[1];try{for(var f=_(u),p=f.next();!p.done;p=f.next()){var d=p.value;k.remove(d)}}catch(t){i={error:t}}finally{try{p&&!p.done&&(o=f.return)&&o.call(f)}finally{if(i)throw i.error}}try{for(var m=_(h),y=m.next();!y.done;y=m.next()){var v=y.value;I.remove(v)}}catch(t){a={error:t}}finally{try{y&&!y.done&&(s=m.return)&&s.call(m)}finally{if(a)throw a.error}}t.i-=e.length+(n?0:7)}w.RequireLoad(t,r)}var k=new(e.AutoloadCommandMap=l)("autoload-macros",{},{}),I=new l("autoload-environments",{},{});e.AutoloadConfiguration=a.Configuration.create("autoload",{handler:{macro:["autoload-macros"],environment:["autoload-environments"]},options:{autoload:c.expandable({action:["toggle","mathtip","texttip"],amsCd:[[],["CD"]],bbox:["bbox"],boldsymbol:["boldsymbol"],braket:["bra","ket","braket","set","Bra","Ket","Braket","Set","ketbra","Ketbra"],cancel:["cancel","bcancel","xcancel","cancelto"],color:["color","definecolor","textcolor","colorbox","fcolorbox"],enclose:["enclose"],extpfeil:["xtwoheadrightarrow","xtwoheadleftarrow","xmapsto","xlongequal","xtofrom","Newextarrow"],html:["href","class","style","cssId"],mhchem:["ce","pu"],newcommand:["newcommand","renewcommand","newenvironment","renewenvironment","def","let"],unicode:["unicode"],verb:["verb"]})},config:function(t,e){var r,n,i,o,a,s,c=e.parseOptions,l=c.handlers.get("macro"),u=c.handlers.get("environment"),h=c.options.autoload;try{for(var f=_(Object.keys(h)),p=f.next();!p.done;p=f.next()){var d=p.value,m=h[d],y=C(2===m.length&&Array.isArray(m[0])?m:[m,[]],2),v=y[0],b=y[1];try{for(var g=(i=void 0,_(v)),M=g.next();!M.done;M=g.next()){var O=M.value;l.lookup(O)&&"color"!==O||k.add(O,new T.Macro(O,A,[d,!0]))}}catch(t){i={error:t}}finally{try{M&&!M.done&&(o=g.return)&&o.call(g)}finally{if(i)throw i.error}}try{for(var x=(a=void 0,_(b)),S=x.next();!S.done;S=x.next()){var E=S.value;u.lookup(E)||I.add(E,new T.Macro(E,A,[d,!1]))}}catch(t){a={error:t}}finally{try{S&&!S.done&&(s=x.return)&&s.call(x)}finally{if(a)throw a.error}}}}catch(t){r={error:t}}finally{try{p&&!p.done&&(n=f.return)&&n.call(f)}finally{if(r)throw r.error}}c.options.require.jax||w.RequireConfiguration.config(t,e)},configPriority:10,init:function(t){t.options.require||c.defaultOptions(t.options,w.RequireConfiguration.options)},priority:10})},function(t,e,r){"use strict";var u=this&&this.__values||function(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(e,"__esModule",{value:!0});var n=r(11),i=r(3),o=r(9),h=r(22),f=r(72);var p=new o.CommandMap("configMacros",{},{});e.ConfigMacrosConfiguration=n.Configuration.create("configMacros",{handler:{macro:["configMacros"]},config:function(t,e){var r,n,i=t.options.macros;try{for(var o=u(Object.keys(i)),a=o.next();!a.done;a=o.next()){var s=a.value,c="string"==typeof i[s]?[i[s]]:i[s],l=Array.isArray(c[2])?new h.Macro(s,f.default.MacroWithTemplate,c.slice(0,2).concat(c[2])):new h.Macro(s,f.default.Macro,c);p.add(s,l)}}catch(t){r={error:t}}finally{try{a&&!a.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}},options:{macros:i.expandable({})}})},function(t,e,r){"use strict";var n;Object.defineProperty(e,"__esModule",{value:!0});var i=r(11),o=r(118),a=r(8);r(247);e.NewcommandConfiguration=i.Configuration.create("newcommand",{handler:{macro:["Newcommand-macros"]},items:(n={},n[o.BeginEnvItem.prototype.kind]=o.BeginEnvItem,n),options:{maxMacros:1e3},init:function(t){t.handler.macro.indexOf(a.ExtensionMaps.NEW_COMMAND)<0&&t.append(i.Configuration.extension())}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(72);new(r(9).CommandMap)("Newcommand-macros",{newcommand:"NewCommand",renewcommand:"NewCommand",newenvironment:"NewEnvironment",renewenvironment:"NewEnvironment",def:"MacroDef",let:"Let"},n.default)},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(11);e.NoUndefinedConfiguration=n.Configuration.create("noundefined",{fallback:{macro:function(t,e){var r=t.create("text","\\"+e);t.Push(t.create("node","mtext",[],{mathcolor:"red"},r))}}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.registerTeX=function(t){if(MathJax.startup){MathJax.startup.registerConstructor("tex",n.TeX),MathJax.startup.useInput("tex"),MathJax.config.tex||(MathJax.config.tex={});var e=MathJax.config.tex.packages;MathJax.config.tex.packages=t,e&&(0,i.insert)(MathJax.config.tex,{packages:e})}};var n=r(105),i=r(3)},function(t,e,r){"use strict";r(251);var n=r(119);MathJax.startup&&(MathJax.startup.registerConstructor("mml",n.MathML),MathJax.startup.useInput("mml"))},function(t,e,r){"use strict";var n=r(5),i=s(r(119)),o=s(r(120)),a=s(r(121));function s(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e.default=t,e}(0,n.combineWithMathJax)({_:{input:{mathml_ts:i,mathml:{FindMathML:o,MathMLCompile:a}}}})},function(t,e,r){"use strict";r(253);var n=r(5),i=r(122);MathJax.loader&&(0,n.combineDefaults)(MathJax.config.loader,"output/chtml",{checkReady:function(){return MathJax.loader.load("output/chtml/fonts/tex")}}),MathJax.startup&&(MathJax.startup.registerConstructor("chtml",i.CHTML),MathJax.startup.useOutput("chtml"))},function(t,e,r){"use strict";var n=r(5),i=mt(r(122)),o=mt(r(1)),a=mt(r(148)),s=mt(r(2)),c=mt(r(125)),l=mt(r(127)),u=mt(r(172)),h=mt(r(174)),f=mt(r(167)),p=mt(r(130)),d=mt(r(146)),m=mt(r(150)),y=mt(r(152)),v=mt(r(168)),b=mt(r(132)),g=mt(r(160)),M=mt(r(136)),O=mt(r(134)),x=mt(r(144)),S=mt(r(155)),E=mt(r(149)),C=mt(r(138)),_=mt(r(142)),T=mt(r(74)),w=mt(r(37)),A=mt(r(162)),k=mt(r(165)),I=mt(r(140)),L=mt(r(164)),N=mt(r(159)),P=mt(r(157)),B=mt(r(170)),R=mt(r(16)),j=mt(r(124)),H=mt(r(23)),D=mt(r(36)),X=mt(r(123)),F=mt(r(128)),W=mt(r(126)),J=mt(r(173)),q=mt(r(175)),V=mt(r(76)),U=mt(r(131)),z=mt(r(147)),G=mt(r(151)),K=mt(r(153)),Z=mt(r(169)),Y=mt(r(133)),$=mt(r(161)),Q=mt(r(137)),tt=mt(r(135)),et=mt(r(145)),rt=mt(r(156)),nt=mt(r(73)),it=mt(r(139)),ot=mt(r(143)),at=mt(r(154)),st=mt(r(38)),ct=mt(r(163)),lt=mt(r(166)),ut=mt(r(141)),ht=mt(r(75)),ft=mt(r(39)),pt=mt(r(158)),dt=mt(r(171));function mt(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e.default=t,e}(0,n.combineWithMathJax)({_:{output:{chtml_ts:i,chtml:{FontData:o,Notation:a,Wrapper:s,WrapperFactory:c,Wrappers_ts:l,Wrappers:{TeXAtom:u,TextNode:h,maction:f,math:p,menclose:d,mfenced:m,mfrac:y,mglyph:v,mi:b,mmultiscripts:g,mn:M,mo:O,mpadded:x,mroot:S,mrow:E,ms:C,mspace:_,msqrt:T,msubsup:w,mtable:A,mtd:k,mtext:I,mtr:L,munderover:N,scriptbase:P,semantics:B}},common:{BBox:R,CssStyles:j,FontData:H,Notation:D,OutputJax:X,Wrapper:F,WrapperFactory:W,Wrappers:{TeXAtom:J,TextNode:q,maction:V,math:U,menclose:z,mfenced:G,mfrac:K,mglyph:Z,mi:Y,mmultiscripts:$,mn:Q,mo:tt,mpadded:et,mroot:rt,mrow:nt,ms:it,mspace:ot,msqrt:at,msubsup:st,mtable:ct,mtd:lt,mtext:ut,mtr:ht,munderover:ft,scriptbase:pt,semantics:dt}}}}})},function(t,e,r){"use strict";var n,i=this&&this.__extends||(n=function(t,e){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),o=this&&this.__assign||function(){return(o=Object.assign||function(t){for(var e,r=1,n=arguments.length;rdocument.body.offsetWidth-5&&(n=document.body.offsetWidth-l.offsetWidth-5),this.post(n,i)},kt.prototype.registerWidget=function(t){this.widgets.push(t)},kt.prototype.unregisterWidget=function(t){var e=this.widgets.indexOf(t);-1document.body.offsetWidth-5&&(i=Math.max(5,i-n-e.offsetWidth+6)),I.prototype.post.call(this,i,o)}},It.prototype.display=function(){this.baseMenu.getFrame().appendChild(this.getHtml())},It.prototype.setBaseMenu=function(){for(var t=this;(t=t.anchor.getMenu())instanceof It;);this.baseMenu=t},L=It,k.SubMenu=L,function(t){t.close=function(t){var e=t.getMenu();e instanceof N.SubMenu?e.baseMenu.unpost():e.unpost()},t.getActiveElement=function(t){var e=t.getMenu();return(e instanceof N.SubMenu?e.baseMenu:e).getStore().getActive()},t.error=function(t,e){console.log("ContextMenu Error: "+e)},t.counter=function(){return e++};var e=0}((N=vt=vt||{}).MenuUtil||(N.MenuUtil={})),P=vt=vt||{},B=P.AbstractEntry,bt(Lt,B),Object.defineProperty(Lt.prototype,"content",{get:function(){return this._content},set:function(t){this._content=t,this.generateHtml(),this.getMenu()&&this.getMenu().generateHtml()},enumerable:!0,configurable:!0}),Lt.prototype.getId=function(){return this.id},Lt.prototype.press=function(){this.disabled||(this.executeAction(),this.executeCallbacks_())},Lt.prototype.executeAction=function(){},Lt.prototype.registerCallback=function(t){-1===this.callbacks.indexOf(t)&&this.callbacks.push(t)},Lt.prototype.unregisterCallback=function(t){var e=this.callbacks.indexOf(t);-1!==e&&this.callbacks.splice(e,1)},Lt.prototype.mousedown=function(t){this.press(),this.stop(t)},Lt.prototype.mouseover=function(t){this.focus(),this.stop(t)},Lt.prototype.mouseout=function(t){this.deactivate(),this.stop(t)},Lt.prototype.generateHtml=function(){B.prototype.generateHtml.call(this);var t=this.getHtml();t.setAttribute("aria-disabled","false"),t.textContent=this.content},Lt.prototype.activate=function(){this.disabled||this.getHtml().classList.add(P.HtmlClasses.MENUACTIVE)},Lt.prototype.deactivate=function(){this.getHtml().classList.remove(P.HtmlClasses.MENUACTIVE)},Lt.prototype.focus=function(){this.getMenu().setFocused(this),B.prototype.focus.call(this),this.activate()},Lt.prototype.unfocus=function(){this.deactivate(),B.prototype.unfocus.call(this)},Lt.prototype.escape=function(t){P.MenuUtil.close(this)},Lt.prototype.up=function(t){this.getMenu().up(t)},Lt.prototype.down=function(t){this.getMenu().down(t)},Lt.prototype.left=function(t){if(this.getMenu()instanceof P.ContextMenu)this.getMenu().left(t);else{var e=this.getMenu();e.setFocused(null),e.getAnchor().focus()}},Lt.prototype.right=function(t){this.getMenu().right(t)},Lt.prototype.space=function(t){this.press()},Lt.prototype.disable=function(){this.disabled=!0;var t=this.getHtml();t.classList.add(P.HtmlClasses.MENUDISABLED),t.setAttribute("aria-disabled","true")},Lt.prototype.enable=function(){this.disabled=!1;var t=this.getHtml();t.classList.remove(P.HtmlClasses.MENUDISABLED),t.removeAttribute("aria-disabled")},Lt.prototype.executeCallbacks_=function(){P.MenuUtil.getActiveElement(this);for(var t=0,e=this.callbacks;t'+this.title+''),r.write("
"+this.generateContent()+"
"),r.write('
'),r.write(""),r.close()):(r.open(),r.write(""+this.title+''),r.write("
"+this.generateContent()+"
"),r.write(""),r.close(),setTimeout(this.resize.bind(this),50))},Jt.prototype.unpost=function(){this.windowList.forEach(function(t){return t.close()}),this.window=null},Jt.prototype.generateContent=function(){return this.content(this.active)},Jt.prototype.resize=function(){var t=this.window.document.body.firstChild,e=this.window.outerHeight-this.window.innerHeight||30,r=this.window.outerWidth-this.window.innerWidth||30;r=Math.max(140,Math.min(Math.floor(.5*this.window.screen.width),t.offsetWidth+r+25)),e=Math.max(40,Math.min(Math.floor(.5*this.window.screen.height),t.offsetHeight+e+25)),this.window.resizeTo(r,e);var n=this.active.getBoundingClientRect();if(n){var i=Math.max(0,Math.min(n.right-Math.floor(r/2),this.window.screen.width-r-20)),o=Math.max(0,Math.min(n.bottom-Math.floor(e/2),this.window.screen.height-e-20));this.window.moveTo(i,o)}this.active=null},Jt.popupSettings={status:"no",toolbar:"no",locationbar:"no",menubar:"no",directories:"no",personalbar:"no",resizable:"yes",scrollbars:"yes",width:400,height:300},yt=Jt,dt.Popup=yt,(vt=vt||{}).TOUCH={START:"touchstart",MOVE:"touchmove",END:"touchend",CANCEL:"touchcancel"}},function(t,e,r){"use strict";var n=r(5),i=l(r(225)),o=l(r(226)),a=l(r(229)),s=l(r(227)),c=l(r(228));function l(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e.default=t,e}(0,n.combineWithMathJax)({_:{ui:{menu:{MJContextMenu:i,Menu:o,MenuHandler:a,MmlVisitor:s,SelectableInfo:c}}}})},function(t,e,r){"use strict";r(78);var n=r(17),i=r(5),o=r(262);(0,i.combineDefaults)(MathJax.config.loader,"dependencies",o.dependencies),(0,i.combineDefaults)(MathJax.config.loader,"paths",o.paths),(0,i.combineDefaults)(MathJax.config.loader,"provides",o.provides),n.Loader.preLoad("loader"),n.Loader.load.apply(n.Loader,function(t){if(Array.isArray(t)){for(var e=0,r=Array(t.length);e