diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 0000000..c34d3c9 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,32 @@ +env: + browser: true + es2020: true +extends: + - standard +parserOptions: + ecmaVersion: 12 +ignorePatterns: + - ".build" + - "build" + - "node_modules/" + - "Vimari Extension/js/lib/**" + - "**/*.xcassets/" +rules: + semi: ["error", "always"] + quotes: ["error", "double", { "avoidEscape": true }] + space-before-function-paren: + [ + "error", + { "anonymous": "always", "named": "never", "asyncArrow": "always" }, + ] + comma-dangle: + [ + "error", + { + "arrays": "only-multiline", + "objects": "only-multiline", + "imports": "never", + "exports": "never", + "functions": "never", + }, + ] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 5807ff8..01d72eb 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,10 +1,9 @@ --- name: Bug report about: Create a report to help us improve -title: '' +title: "" labels: bug -assignees: '' - +assignees: "" --- **Describe the bug** @@ -12,6 +11,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -35,9 +35,9 @@ printf -- "- macOS version: %s\n- Safari version: %s\n- Vimari version: %s\n" "$ --> - - macOS version: - - Safari version: - - Vimari version: +- macOS version: +- Safari version: +- Vimari version: **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index e301d68..51d1465 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,10 +1,9 @@ --- name: Feature request about: Suggest an idea for this project -title: '' +title: "" labels: feature request -assignees: '' - +assignees: "" --- **Is your feature request related to a problem? Please describe.** diff --git a/.github/workflows/js-code-checks.yml b/.github/workflows/js-code-checks.yml new file mode 100644 index 0000000..e141411 --- /dev/null +++ b/.github/workflows/js-code-checks.yml @@ -0,0 +1,39 @@ +name: JavaScript Code Checks + +on: + pull_request: + paths: + - '.github/workflows/js-code-checks.yml' + - '.eslintrc.yml' + - '.prettierigore' + - '.prettierrc.json' + - '**/*.(js|json|css|md)' + +jobs: + format: + runs-on: ubuntu-latest + steps: + # Setup + - uses: actions/checkout@v2 + with: + # Make sure the actual branch is checked out when running on pull requests + ref: ${{ github.head_ref }} + + # Run Prettier in linting mode. + - name: Prettier + uses: creyD/prettier_action@v3.0 + with: + # Report unprettified files. + prettier_options: --check **/*.js + + lint: + runs-on: ubuntu-latest + steps: + # Setup + - uses: actions/checkout@v2 + + # Run ESLint + - name: ESLint + uses: bradennapier/eslint-plus-action@v3.4.2 + with: + issueSummary: false diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 8ae0675..ae76b89 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -1,10 +1,9 @@ name: Node CI -on: [push] +on: pull_request jobs: build: - runs-on: ubuntu-latest strategy: @@ -12,13 +11,13 @@ jobs: node-version: [12.x] steps: - - uses: actions/checkout@v1 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: make all test - run: | - make all test - env: - CI: true + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: make all test + run: | + make all test + env: + CI: true diff --git a/.github/workflows/swift-code-checks.yml b/.github/workflows/swift-code-checks.yml new file mode 100644 index 0000000..5f15413 --- /dev/null +++ b/.github/workflows/swift-code-checks.yml @@ -0,0 +1,38 @@ +name: Swift Code Checks + +on: + pull_request: + paths: + - '.github/workflows/swift-code-checks.yml' + - '.swiftformat.yml' + - '.swiftlint.yml' + - '**/*.swift' + +jobs: + format: + runs-on: ubuntu-latest + steps: + # Setup + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: .build + key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} + restore-keys: | + ${{ runner.os }}-spm- + + # Run SwiftFormat in linting mode. + - name: SwiftFormat + run: swift run swiftformat --lint . + + lint: + runs-on: ubuntu-latest + steps: + # Setup + - uses: actions/checkout@v2 + + # Use custom SwiftLint action for GitHub inline comments. + - name: SwiftLint + uses: norio-nomura/action-swiftlint@3.1.0 + env: + DIFF_BASE: ${{ github.base_ref }} diff --git a/.gitignore b/.gitignore index 855c269..ff8aca7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ node_modules/ ## Build generated build/ DerivedData/ +.build/ ## Various settings xcuserdata/ diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..da07c5a --- /dev/null +++ b/.prettierignore @@ -0,0 +1,12 @@ +# Ignore build artifacts +.build/ +build/ + +# Ignore packages +node_modules/ + +# Ignore imported libraries +Vimari Extension/js/lib/ + +# Ignore any asset files +**/*.xcassets/ \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1 @@ +{} diff --git a/.swiftformat b/.swiftformat new file mode 100644 index 0000000..b8e58a4 --- /dev/null +++ b/.swiftformat @@ -0,0 +1,2 @@ +--disable wrapMultilineStatementBraces +--exclude .build/ \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..6eca2d5 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,5 @@ +disabled_rules: + # Trailing comma conflicts with swift-format + - trailing_comma + +excluded: .build/** diff --git a/CHANGELOG.md b/CHANGELOG.md index 0be0d84..58ab6ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,65 +1,73 @@ -Changelog -------------- +## Changelog ### Unreleased -* Remove eager link hint triggering [#190](https://github.com/televator-apps/vimari/issues/190) -* Use `window.open` for `openNewTab` action [#189](https://github.com/televator-apps/vimari/issues/189) -* Add user customisation (based on the work of @nieldm [#163](https://github.com/televator-apps/vimari/pull/163)). -* Update Vimari interface to allow users access to their configuration. -* Remove `closeTabReverse` action. -* Normal mode now isolates keybindings from the underlying website, this means that to interact with the underlying website you need to enter insert mode. -* You can enter insert mode by pressing i and exit the mode by pressing esc. Activating either mode will display the HUD. -* In insert mode Vimari keybindings are disabled (except for esc which brings you back to normal mode) allowing you to interact with the underlying website. -* Add `goToFirstInput` action on g i by default (by [isundaylee](https://github.com/isundaylee)). -* Add smooth scrolling (based on sVim implementation). + +- Remove eager link hint triggering [#190](https://github.com/televator-apps/vimari/issues/190) +- Use `window.open` for `openNewTab` action [#189](https://github.com/televator-apps/vimari/issues/189) +- Add user customisation (based on the work of @nieldm [#163](https://github.com/televator-apps/vimari/pull/163)). +- Update Vimari interface to allow users access to their configuration. +- Remove `closeTabReverse` action. +- Normal mode now isolates keybindings from the underlying website, this means that to interact with the underlying website you need to enter insert mode. +- You can enter insert mode by pressing i and exit the mode by pressing esc. Activating either mode will display the HUD. +- In insert mode Vimari keybindings are disabled (except for esc which brings you back to normal mode) allowing you to interact with the underlying website. +- Add `goToFirstInput` action on g i by default (by [isundaylee](https://github.com/isundaylee)). +- Add smooth scrolling (based on sVim implementation). ### 2.0.3 (2019-09-26) -* Fix newTabHintToggle to use shift+f instead of F -* Implement forward tab and backward tab commands. -* Close tab with x is now implemented. Note that this relies on Safari's default behaviour to choose whether to switch to the left or right tab after closing the current tab. +- Fix newTabHintToggle to use shift+f instead of F +- Implement forward tab and backward tab commands. +- Close tab with x is now implemented. Note that this relies on Safari's default behaviour to choose whether to switch to the left or right tab after closing the current tab. ### 2.0.2 (2019-09-23) -* Release a signed, notarized App and Safari App Extension -* Reverse link hints, so nearby links have different hints [#77](https://github.com/televator-apps/vimari/issues/77) -* Hide non-matching link hints [#79](https://github.com/televator-apps/vimari/issues/79) -* Show state of extension in main application +- Release a signed, notarized App and Safari App Extension +- Reverse link hints, so nearby links have different hints [#77](https://github.com/televator-apps/vimari/issues/77) +- Hide non-matching link hints [#79](https://github.com/televator-apps/vimari/issues/79) +- Show state of extension in main application ### 2.0.0 (14/7/2018) -* vimari now exists as a Safari App Extension, making it compatible with Safari + +- vimari now exists as a Safari App Extension, making it compatible with Safari version 12 ### 1.13.0 (16/8/2018) -* New fresh icon -* Removed shift as default modifier key -* 't' now opens new tab -* HUD now looks nicer -* Open link in new tab now works (bugfix) -* Excluded URL doesn't need to be exact anymore (bugfix) + +- New fresh icon +- Removed shift as default modifier key +- 't' now opens new tab +- HUD now looks nicer +- Open link in new tab now works (bugfix) +- Excluded URL doesn't need to be exact anymore (bugfix) ### 1.2 - 1.12 skipped ### 1.1 (31/07/2011) -* Updated to work with the new version of Safari on lion -* Removed history forward / back -* Changed directory structure to make it more developer friendly + +- Updated to work with the new version of Safari on lion +- Removed history forward / back +- Changed directory structure to make it more developer friendly ### 1.0 (21/11/2010) -* Changed the way vimari modifier keys work. ESC key depricated. Now use CTRL-modifierkey. + +- Changed the way vimari modifier keys work. ESC key depricated. Now use CTRL-modifierkey. ### 0.4 (17/11/2010) -* First BETA release ! -* Press ESC to enter a permanent state of 'non' insert mode. Clicking on any input then exits insert mode. This fixes several issues with google and facebook. + +- First BETA release ! +- Press ESC to enter a permanent state of 'non' insert mode. Clicking on any input then exits insert mode. This fixes several issues with google and facebook. ### 0.3 (16/11/2010) -* Moved the extension startup code to be loaded before the browser page. Events can now be intercepted before they are passed to the browser page. -* Created a manifest file, this allows automatic updates to take place. -* Added insert mode. If the selected node can accept an input, the extension is disabled. This functionality still needs some work. -* Ported the HUD from vimium. The hud displays information along the bottom of the screen. The hud has been ported but is not used for very much at the moment. + +- Moved the extension startup code to be loaded before the browser page. Events can now be intercepted before they are passed to the browser page. +- Created a manifest file, this allows automatic updates to take place. +- Added insert mode. If the selected node can accept an input, the extension is disabled. This functionality still needs some work. +- Ported the HUD from vimium. The hud displays information along the bottom of the screen. The hud has been ported but is not used for very much at the moment. ### 0.2 (14/11/2010) -* Pressing ESC now removes focus from any input fields and activates modifiers + +- Pressing ESC now removes focus from any input fields and activates modifiers ### 0.1 (14/11/1020) -* First alpa release of vimari. Added basic features but still very buggy. + +- First alpa release of vimari. Added basic features but still very buggy. diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..4fa65ad --- /dev/null +++ b/Package.resolved @@ -0,0 +1,88 @@ +{ + "object": { + "pins": [ + { + "package": "Commandant", + "repositoryURL": "https://github.com/Carthage/Commandant.git", + "state": { + "branch": null, + "revision": "ab68611013dec67413628ac87c1f29e8427bc8e4", + "version": "0.17.0" + } + }, + { + "package": "Nimble", + "repositoryURL": "https://github.com/Quick/Nimble.git", + "state": { + "branch": null, + "revision": "2b1809051b4a65c1d7f5233331daa24572cd7fca", + "version": "8.1.1" + } + }, + { + "package": "Quick", + "repositoryURL": "https://github.com/Quick/Quick.git", + "state": { + "branch": null, + "revision": "09b3becb37cb2163919a3842a4c5fa6ec7130792", + "version": "2.2.1" + } + }, + { + "package": "SourceKitten", + "repositoryURL": "https://github.com/jpsim/SourceKitten.git", + "state": { + "branch": null, + "revision": "77a4dbbb477a8110eb8765e3c44c70fb4929098f", + "version": "0.29.0" + } + }, + { + "package": "SwiftFormat", + "repositoryURL": "https://github.com/nicklockwood/SwiftFormat", + "state": { + "branch": null, + "revision": "e5675ae49383e0d248d495bcfbedaa6f9b1f5375", + "version": "0.45.6" + } + }, + { + "package": "SwiftLint", + "repositoryURL": "https://github.com/Realm/SwiftLint", + "state": { + "branch": null, + "revision": "da66a81710714112d51041264440987b992849c3", + "version": "0.40.0" + } + }, + { + "package": "SwiftyTextTable", + "repositoryURL": "https://github.com/scottrhoyt/SwiftyTextTable.git", + "state": { + "branch": null, + "revision": "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3", + "version": "0.9.0" + } + }, + { + "package": "SWXMLHash", + "repositoryURL": "https://github.com/drmohundro/SWXMLHash.git", + "state": { + "branch": null, + "revision": "a4931e5c3bafbedeb1601d3bb76bbe835c6d475a", + "version": "5.0.1" + } + }, + { + "package": "Yams", + "repositoryURL": "https://github.com/jpsim/Yams.git", + "state": { + "branch": null, + "revision": "c947a306d2e80ecb2c0859047b35c73b8e1ca27f", + "version": "2.0.0" + } + } + ] + }, + "version": 1 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..b7cc430 --- /dev/null +++ b/Package.swift @@ -0,0 +1,22 @@ +// swift-tools-version:5.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "vimari", + products: [ + .library(name: "Vimari", targets: ["Vimari"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + .package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.45.6"), + .package(url: "https://github.com/Realm/SwiftLint", from: "0.40.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target(name: "Vimari", dependencies: [], path: "Vimari"), + ] +) diff --git a/README.md b/README.md index 4090806..7027f2a 100644 --- a/README.md +++ b/README.md @@ -15,26 +15,27 @@ best components of vimium and adapting them to Safari. ## Releases -### Safari 12 and 13 +### Safari 12+ [![Download on the Mac App Store](assets/Download_on_the_Mac_App_Store_Badge_US.svg)](https://apps.apple.com/us/app/vimari/id1480933944?ls=1&mt=12) ### Safari 11 and below (DEPRECATED) - - [v1.13](https://github.com/guyht/vimari/releases/tag/v1.13) - - [v1.12](https://github.com/guyht/vimari/releases/tag/v1.12) - - [v1.11](https://github.com/guyht/vimari/releases/tag/v1.11) - - [v1.10](https://github.com/guyht/vimari/releases/tag/v1.10) - - [v1.9](https://github.com/guyht/vimari/releases/tag/v1.9) + +- [v1.13](https://github.com/guyht/vimari/releases/tag/v1.13) +- [v1.12](https://github.com/guyht/vimari/releases/tag/v1.12) +- [v1.11](https://github.com/guyht/vimari/releases/tag/v1.11) +- [v1.10](https://github.com/guyht/vimari/releases/tag/v1.10) +- [v1.9](https://github.com/guyht/vimari/releases/tag/v1.9) ## Installation -### Safari 12 and 13 (macOS Mojave and Catalina) +### Safari 12+ #### Mac App Store 1. [Download Vimari](https://apps.apple.com/us/app/vimari/id1480933944?ls=1&mt=12) for free from the Mac App Store 2. Launch Vimari.app -3. Click "Open in Safari Extensions Preferences...", Safari's Extension Preferences should open +3. Click "Open in Safari Extensions Preferences", Safari's Extension Preferences should open 4. Make sure that the checkbox for the Vimari extension is ticked 5. Go back to Vimari.app and press the reload button to check the status of the app. If it says "Enabled" then it is ready. 6. You may need to relaunch Safari for the extension to work @@ -50,8 +51,6 @@ best components of vimium and adapting them to Safari. 7. Go back to Vimari.app and press the reload button to check the status of the app. If it says "Enabled" then it is ready. 8. You may need to relaunch Safari for the extension to work - - ### Safari 11 (legacy Safari Extension method) [Download the Vimari 1.13](https://github.com/guyht/vimari/releases/tag/v1.13) and double-click @@ -60,6 +59,7 @@ the file. ## Usage ### Settings + **Command Prefix** - Modifier key to hold down with your action key. If you leave it blank you don't need to hold down anything (default setting). @@ -80,6 +80,7 @@ for HTML elements having cursor style set to "pointer". ### Keyboard Bindings #### In-page navigation + f Toggle links F Toggle links (open link in new tab) k Scroll up @@ -92,6 +93,7 @@ for HTML elements having cursor style set to "pointer". G Go to bottom of page #### Page/Tab navigation + H History back L History forward r Reload diff --git a/SETUP_README.md b/SETUP_README.md new file mode 100644 index 0000000..beebcc8 --- /dev/null +++ b/SETUP_README.md @@ -0,0 +1,58 @@ +# Vimari Setup + +If you interested in contributing to Vimari or are installing Vimari from source you can follow this guide on setting up the project and some of the tooling around it. + +## Linting & Formatting + +### SwiftFormat + +Vimari uses [SwiftFormat](https://github.com/nicklockwood/SwiftFormat) to format Swift code. + +```bash +# Run SwiftFormat +cd path/to/vimari/repo +swift run swiftformat . + +# Or if you don't want SwiftFormat to automatically format your changes but +# instead display them to you. +swift run swiftformat --lint . +``` + +### SwiftLint + +Vimari uses [SwiftLint](https://github.com/realm/SwiftLint) to lint Swift code. + +```bash +# Run SwiftLint +cd path/to/vimari/repo +swift run swiftlint + +# Let SwiftLint fix issues where it can +swift run swiftlint autocorrect +``` + +### Prettier + +Vimari uses [Prettier](https://prettier.io/) to format JavaScript, HTML, JSON and CSS files. + +```bash +# Run Prettier +cd path/to/vimari/repo +npx prettier --check . + +# Let Prettier do the formatting for you +npx prettier --write . +``` + +### ESLint + +Vimari uses [ESLint](https://eslint.org/) to lint JavaScript code. + +```bash +# Run ESLint +cd path/to/vimari/repo +npx eslint . + +# Let ESLint fix issues where it can +npx eslint --fix . +``` diff --git a/Vimari Extension/ConfigurationModel.swift b/Vimari Extension/ConfigurationModel.swift index 879b677..a36fbc2 100644 --- a/Vimari Extension/ConfigurationModel.swift +++ b/Vimari Extension/ConfigurationModel.swift @@ -10,14 +10,13 @@ protocol ConfigurationModelProtocol { func editConfigFile() throws func resetConfigFile() throws func getDefaultSettings() throws -> [String: Any] - func getUserSettings() throws -> [String : Any] + func getUserSettings() throws -> [String: Any] } import Foundation import SafariServices class ConfigurationModel: ConfigurationModelProtocol { - private enum Constant { static let settingsFileName = "defaultSettings" static let userSettingsFileName = "userSettings" @@ -27,7 +26,7 @@ class ConfigurationModel: ConfigurationModelProtocol { let userSettingsUrl: URL = FileManager.documentDirectoryURL .appendingPathComponent(Constant.userSettingsFileName) .appendingPathExtension("json") - + func editConfigFile() throws { let settingsFilePath = try findOrCreateUserSettings() NSWorkspace.shared.openFile( @@ -35,7 +34,7 @@ class ConfigurationModel: ConfigurationModelProtocol { withApplication: Constant.defaultEditor ) } - + func resetConfigFile() throws { let settingsFilePath = try overwriteUserSettings() NSWorkspace.shared.openFile( @@ -43,23 +42,23 @@ class ConfigurationModel: ConfigurationModelProtocol { withApplication: Constant.defaultEditor ) } - - func getDefaultSettings() throws -> [String : Any] { + + func getDefaultSettings() throws -> [String: Any] { return try loadSettings(fromFile: Constant.settingsFileName) } - - func getUserSettings() throws -> [String : Any] { + + func getUserSettings() throws -> [String: Any] { let userFilePath = try findOrCreateUserSettings() let urlSettingsFile = URL(fileURLWithPath: userFilePath) let settingsData = try Data(contentsOf: urlSettingsFile) return try settingsData.toJSONObject() } - - private func loadSettings(fromFile file: String) throws -> [String : Any] { + + private func loadSettings(fromFile file: String) throws -> [String: Any] { let settingsData = try Bundle.main.getJSONData(from: file) return try settingsData.toJSONObject() } - + private func findOrCreateUserSettings() throws -> String { let url = userSettingsUrl let urlString = url.path @@ -70,7 +69,7 @@ class ConfigurationModel: ConfigurationModelProtocol { try data.write(to: url) return urlString } - + private func overwriteUserSettings() throws -> String { let url = userSettingsUrl let urlString = userSettingsUrl.path @@ -97,14 +96,14 @@ private extension Data { private extension Bundle { func getJSONPath(for file: String) throws -> String { - guard let result = self.path(forResource: file, ofType: ".json") else { + guard let result = path(forResource: file, ofType: ".json") else { throw DataError.notFound } return result } - + func getJSONData(from file: String) throws -> Data { - let settingsPath = try self.getJSONPath(for: file) + let settingsPath = try getJSONPath(for: file) let urlSettingsFile = URL(fileURLWithPath: settingsPath) return try Data(contentsOf: urlSettingsFile) } @@ -112,7 +111,13 @@ private extension Bundle { private extension FileManager { static var documentDirectoryURL: URL { - let documentDirectoryURL = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false) + // swiftlint:disable:next force_try + let documentDirectoryURL = try! FileManager.default.url( + for: .applicationSupportDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: false + ) return documentDirectoryURL } } diff --git a/Vimari Extension/SafariExtensionHandler.swift b/Vimari Extension/SafariExtensionHandler.swift index 3b54422..672cd98 100644 --- a/Vimari Extension/SafariExtensionHandler.swift +++ b/Vimari Extension/SafariExtensionHandler.swift @@ -1,7 +1,6 @@ import SafariServices enum ActionType: String { - case openLinkInTab case tabForward case tabBackward case closeTab @@ -19,18 +18,17 @@ enum TabDirection: String { } class SafariExtensionHandler: SFSafariExtensionHandler { - private enum Constant { static let mainAppName = "Vimari" - static let newTabPageURL = "https://duckduckgo.com" //Try it :D + static let newTabPageURL = "https://duckduckgo.com" // Try it :D } - + let configuration: ConfigurationModelProtocol = ConfigurationModel() - - //MARK: Overrides + + // MARK: Overrides // This method handles messages from the Vimari App (located /Vimari in the repository) - override func messageReceivedFromContainingApp(withName messageName: String, userInfo: [String : Any]? = nil) { + override func messageReceivedFromContainingApp(withName messageName: String, userInfo _: [String: Any]? = nil) { do { switch InputAction(rawValue: messageName) { case .openSettings: @@ -43,16 +41,12 @@ class SafariExtensionHandler: SFSafariExtensionHandler { } catch { NSLog(error.localizedDescription) } - } // This method handles messages from the extension (in the browser page) - override func messageReceived(withName messageName: String, from page: SFSafariPage, userInfo: [String: Any]?) { + override func messageReceived(withName messageName: String, from page: SFSafariPage, userInfo _: [String: Any]?) { NSLog("Received message: \(messageName)") switch ActionType(rawValue: messageName) { - case .openLinkInTab: - let url = URL(string: userInfo?["url"] as! String) - openInNewTab(url: url!) case .tabForward: changeTab(withDirection: .forward, from: page) case .tabBackward: @@ -73,16 +67,18 @@ class SafariExtensionHandler: SFSafariExtensionHandler { } override func validateToolbarItem(in _: SFSafariWindow, validationHandler: @escaping ((Bool, String) -> Void)) { - // This is called when Safari's state changed in some way that would require the extension's toolbar item to be validated again. + // This is called when Safari's state changed in some way that + // would require the extension's toolbar item to be validated + // again. validationHandler(true, "") } override func popoverViewController() -> SFSafariExtensionViewController { return SafariExtensionViewController.shared } - + // MARK: Tabs Methods - + private func openInNewTab(url: URL) { SFSafariApplication.getActiveWindow { activeWindow in activeWindow?.openTab(with: url, makeActiveIfPossible: false, completionHandler: { _ in @@ -91,11 +87,14 @@ class SafariExtensionHandler: SFSafariExtensionHandler { } } - private func changeTab(withDirection direction: TabDirection, from page: SFSafariPage, completionHandler: (() -> Void)? = nil ) { - page.getContainingTab() { currentTab in - // Using .currentWindow instead of .containingWindow, this prevents the window being nil in the case of a pinned tab. + private func changeTab(withDirection direction: TabDirection, + from page: SFSafariPage, completionHandler: (() -> Void)? = nil) { + page.getContainingTab { currentTab in + // Using .currentWindow instead of .containingWindow, this + // prevents the window being nil in the case of a pinned + // tab. self.currentWindow(from: page) { window in - window?.getAllTabs() { tabs in + window?.getAllTabs { tabs in tabs.forEach { tab in NSLog(tab.description) } if let currentIndex = tabs.firstIndex(of: currentTab) { let indexStep = direction == TabDirection.forward ? 1 : -1 @@ -106,7 +105,6 @@ class SafariExtensionHandler: SFSafariExtensionHandler { let newIndex = mod(currentIndex + indexStep, tabs.count) tabs[newIndex].activate(completionHandler: completionHandler ?? {}) - } } } @@ -117,24 +115,23 @@ class SafariExtensionHandler: SFSafariExtensionHandler { Returns the containing window of a SFSafariPage, if not available default to the current active window. */ private func currentWindow(from page: SFSafariPage, completionHandler: @escaping ((SFSafariWindow?) -> Void)) { - page.getContainingTab() { $0.getContainingWindow() { window in + page.getContainingTab { $0.getContainingWindow { window in if window != nil { return completionHandler(window) } else { - SFSafariApplication.getActiveWindow() { window in - return completionHandler(window) + SFSafariApplication.getActiveWindow { window in + completionHandler(window) } } }} } - + private func closeTab(from page: SFSafariPage) { - page.getContainingTab { - tab in + page.getContainingTab { tab in tab.close() } } - + // MARK: Settings private func getSetting(_ settingKey: String) -> Any? { @@ -146,7 +143,7 @@ class SafariExtensionHandler: SFSafariExtensionHandler { return nil } } - + private func updateSettings(page: SFSafariPage) { do { let settings: [String: Any] @@ -160,7 +157,7 @@ class SafariExtensionHandler: SFSafariExtensionHandler { NSLog(error.localizedDescription) } } - + private func fallbackSettings(page: SFSafariPage) { do { let settings = try configuration.getUserSettings() @@ -173,6 +170,7 @@ class SafariExtensionHandler: SFSafariExtensionHandler { // MARK: Helpers +// swiftlint:disable identifier_name private func mod(_ a: Int, _ n: Int) -> Int { // https://stackoverflow.com/questions/41180292/negative-number-modulo-in-swift precondition(n > 0, "modulus must be positive") @@ -180,9 +178,11 @@ private func mod(_ a: Int, _ n: Int) -> Int { return r >= 0 ? r : r + n } +// swiftlint:enable identifier_name + private extension SFSafariPage { func dispatch(settings: [String: Any]) { - self.dispatchMessageToScript( + dispatchMessageToScript( withName: "updateSettingsEvent", userInfo: settings ) @@ -198,4 +198,3 @@ private extension SFSafariApplication { } } } - diff --git a/Vimari Extension/css/injected.css b/Vimari Extension/css/injected.css index 3d20791..84cbb9b 100644 --- a/Vimari Extension/css/injected.css +++ b/Vimari Extension/css/injected.css @@ -7,7 +7,7 @@ cursor: auto; display: inline; float: none; - font-family : "Helvetica Neue", "Helvetica", "Arial", sans-serif; + font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif; font-size: inherit; font-style: normal; font-variant: normal; @@ -62,7 +62,8 @@ div.internalVimiumHintMarker > .matchingCharacter { color: #dcbc2a; } -.vimiumHUD, .vimiumHUD * { +.vimiumHUD, +.vimiumHUD * { line-height: 100%; font-size: 11px; font-weight: normal; @@ -84,31 +85,31 @@ div.internalVimiumHintMarker > .matchingCharacter { font-family: Lucida Grande, Arial, Sans; /* One less than vimium's hint markers, so link hints can be shown e.g. for the panel's close button. */ z-index: 99999998; - text-shadow: 0px 1px 2px #FFF; - line-height: 1.0; + text-shadow: 0px 1px 2px #fff; + line-height: 1; opacity: 0; } -.vimiumHUD a, .vimiumHUD a:hover { +.vimiumHUD a, +.vimiumHUD a:hover { background: transparent; color: blue; text-decoration: underline; } .vimiumHUD a.close-button { - float:right; - font-family:courier new; - font-weight:bold; - color:#9C9A9A; - text-decoration:none; - padding-left:10px; - margin-top:-1px; - font-size:14px; + float: right; + font-family: courier new; + font-weight: bold; + color: #9c9a9a; + text-decoration: none; + padding-left: 10px; + margin-top: -1px; + font-size: 14px; } .vimiumHUD a.close-button:hover { - color:#333333; - cursor:default; - -webkit-user-select:none; + color: #333333; + cursor: default; + -webkit-user-select: none; } - diff --git a/Vimari Extension/js/SafariExtensionCommunicator.js b/Vimari Extension/js/SafariExtensionCommunicator.js index 20c1e96..6464c7a 100644 --- a/Vimari Extension/js/SafariExtensionCommunicator.js +++ b/Vimari Extension/js/SafariExtensionCommunicator.js @@ -1,27 +1,31 @@ -var SafariExtensionCommunicator = (function (msgHandler) { - 'use strict' - var publicAPI = {} +/* global safari */ - // Connect the provided message handler to the received messages. - safari.self.addEventListener("message", msgHandler) +// The variable is used in other files. +// eslint-disable-next-line no-unused-vars +var SafariExtensionCommunicator = function (msgHandler) { + "use strict"; + var publicAPI = {}; - var sendMessage = function(msgName) { - safari.extension.dispatchMessage(msgName) - } + // Connect the provided message handler to the received messages. + safari.self.addEventListener("message", msgHandler); - publicAPI.requestSettingsUpdate = function() { - sendMessage("updateSettings") - } - publicAPI.requestTabForward = function() { - sendMessage("tabForward") - } - publicAPI.requestTabBackward = function() { - sendMessage("tabBackward") - } - publicAPI.requestCloseTab = function () { - sendMessage("closeTab") - } + var sendMessage = function (msgName) { + safari.extension.dispatchMessage(msgName); + }; - // Return only the public methods. - return publicAPI; -}); + publicAPI.requestSettingsUpdate = function () { + sendMessage("updateSettings"); + }; + publicAPI.requestTabForward = function () { + sendMessage("tabForward"); + }; + publicAPI.requestTabBackward = function () { + sendMessage("tabBackward"); + }; + publicAPI.requestCloseTab = function () { + sendMessage("closeTab"); + }; + + // Return only the public methods. + return publicAPI; +}; diff --git a/Vimari Extension/js/injected.js b/Vimari Extension/js/injected.js index e24d1de..9676622 100644 --- a/Vimari Extension/js/injected.js +++ b/Vimari Extension/js/injected.js @@ -1,3 +1,5 @@ +/* global Mousetrap, activateLinkHintsMode, deactivateLinkHintsMode, HUD, linkHintsModeActivated, customScrollBy, SafariExtensionCommunicator */ + /* * Vimari injected script. * @@ -6,126 +8,154 @@ * therefore we can stop certain pages (google) stealing the focus. */ - /* * Global vars * * topWindow - true if top window, false if iframe * settings - stores user settings * currentZoomLevel - required for vimium scripts to run correctly - * linkHintCss - required from vimium scripts * extensionActive - is the extension currently enabled (should only be true when tab is active) * shiftKeyToggle - is shift key currently toggled */ -var topWindow = (window.top === window), - settings = {}, - currentZoomLevel = 100, - linkHintCss = {}, - extensionActive = true, - insertMode = false, - shiftKeyToggle = false, - hudDuration = 5000, - extensionCommunicator = SafariExtensionCommunicator(messageHandler); - -var actionMap = { - 'hintToggle' : function() { - HUD.showForDuration('Open link in current tab', hudDuration); - activateLinkHintsMode(false, false); }, - - 'newTabHintToggle' : function() { - HUD.showForDuration('Open link in new tab', hudDuration); - activateLinkHintsMode(true, false); }, - - 'tabForward': - function() { extensionCommunicator.requestTabForward(); }, - - 'tabBack': - function() { extensionCommunicator.requestTabBackward() }, - - 'scrollDown': - function() { customScrollBy(0, settings.scrollSize); }, - - 'scrollUp': - function() { customScrollBy(0, -settings.scrollSize); }, - - 'scrollLeft': - function() { customScrollBy(-settings.scrollSize, 0); }, - - 'scrollRight': - function() { customScrollBy(settings.scrollSize, 0); }, - - 'goBack': - function() { window.history.back(); }, - - 'goForward': - function() { window.history.forward(); }, - - 'reload': - function() { window.location.reload(); }, +var topWindow = window.top === window; +var settings = {}; +var extensionActive = true; +var insertMode = false; +var hudDuration = 5000; +var extensionCommunicator = SafariExtensionCommunicator(messageHandler); - 'openTab': - function() { window.open(settings.openTabUrl); }, +// The variables are used in other files. +/* eslint-disable no-unused-vars */ +var currentZoomLevel = 100; +var shiftKeyToggle = false; +/* eslint-enable */ - 'closeTab': - function() { extensionCommunicator.requestCloseTab(); }, - - 'scrollDownHalfPage': - function() { customScrollBy(0, window.innerHeight / 2); }, - - 'scrollUpHalfPage': - function() { customScrollBy(0, window.innerHeight / -2); }, - - 'goToPageBottom': - function() { customScrollBy(0, document.body.scrollHeight); }, - - 'goToPageTop': - function() { customScrollBy(0, -document.body.scrollHeight); }, - - 'goToFirstInput': - function() { goToFirstInput(); } +var actionMap = { + hintToggle: function () { + HUD.showForDuration("Open link in current tab", hudDuration); + activateLinkHintsMode(false, false); + }, + + newTabHintToggle: function () { + HUD.showForDuration("Open link in new tab", hudDuration); + activateLinkHintsMode(true, false); + }, + + tabForward: function () { + extensionCommunicator.requestTabForward(); + }, + + tabBack: function () { + extensionCommunicator.requestTabBackward(); + }, + + scrollDown: function () { + customScrollBy(0, settings.scrollSize); + }, + + scrollUp: function () { + customScrollBy(0, -settings.scrollSize); + }, + + scrollLeft: function () { + customScrollBy(-settings.scrollSize, 0); + }, + + scrollRight: function () { + customScrollBy(settings.scrollSize, 0); + }, + + goBack: function () { + window.history.back(); + }, + + goForward: function () { + window.history.forward(); + }, + + reload: function () { + window.location.reload(); + }, + + openTab: function () { + window.open(settings.openTabUrl); + }, + + closeTab: function () { + extensionCommunicator.requestCloseTab(); + }, + + scrollDownHalfPage: function () { + customScrollBy(0, window.innerHeight / 2); + }, + + scrollUpHalfPage: function () { + customScrollBy(0, window.innerHeight / -2); + }, + + goToPageBottom: function () { + customScrollBy(0, document.body.scrollHeight); + }, + + goToPageTop: function () { + customScrollBy(0, -document.body.scrollHeight); + }, + + goToFirstInput: function () { + goToFirstInput(); + }, }; // Inspiration and general algorithm taken from sVim. function goToFirstInput() { - var inputs = document.querySelectorAll('input,textarea'); + var inputs = document.querySelectorAll("input,textarea"); var bestInput = null; var bestInViewInput = null; - inputs.forEach(function(input) { + inputs.forEach(function (input) { // Skip if hidden or disabled - if ((input.offsetParent === null) || - input.disabled || - (input.getAttribute('type') === 'hidden') || - (getComputedStyle(input).visibility === 'hidden') || - (input.getAttribute('display') === 'none')) { + if ( + input.offsetParent === null || + input.disabled || + input.getAttribute("type") === "hidden" || + getComputedStyle(input).visibility === "hidden" || + input.getAttribute("display") === "none" + ) { return; } // Skip things that are not actual inputs - if ((input.localName !== 'textarea') && - (input.localName !== 'input') && - (input.getAttribute('contenteditable') !== 'true')) { + if ( + input.localName !== "textarea" && + input.localName !== "input" && + input.getAttribute("contenteditable") !== "true" + ) { return; } // Skip non-text inputs - if (/button|radio|file|image|checkbox|submit/i.test(input.getAttribute('type'))) { + if ( + /button|radio|file|image|checkbox|submit/i.test( + input.getAttribute("type") + ) + ) { return; } var inputRect = input.getClientRects()[0]; - var isInView = (inputRect.top >= -inputRect.height) && - (inputRect.top <= window.innerHeight) && - (inputRect.left >= -inputRect.width) && - (inputRect.left <= window.innerWidth); + var isInView = + inputRect.top >= -inputRect.height && + inputRect.top <= window.innerHeight && + inputRect.left >= -inputRect.width && + inputRect.left <= window.innerWidth; if (bestInput === null) { bestInput = input; } - if (isInView && (bestInViewInput === null)) { + if (isInView && bestInViewInput === null) { bestInViewInput = input; } }); @@ -137,136 +167,133 @@ function goToFirstInput() { } // Meant to be overridden, but still has to be copy/pasted from the original... -Mousetrap.prototype.stopCallback = function(e, element, combo) { - // Escape key is special, no need to stop. Vimari-specific. - if (combo === 'esc' || combo === 'ctrl+[') { return false; } +// eslint-disable-next-line no-prototype-builtins +Mousetrap.prototype.stopCallback = function (e, element, combo) { + // Escape key is special, no need to stop. Vimari-specific. + if (combo === "esc" || combo === "ctrl+[") { + return false; + } // Preserve the behavior of allowing ex. ctrl-j in an input - if (settings.modifier) { return false; } - - // if the element has the class "mousetrap" then no need to stop - if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) { - return false; - } + if (settings.modifier) { + return false; + } - var tagName = element.tagName; - var contentIsEditable = (element.contentEditable && element.contentEditable === 'true'); + // if the element has the class "mousetrap" then no need to stop + if ((" " + element.className + " ").indexOf(" mousetrap ") > -1) { + return false; + } - // stop for input, select, and textarea - return tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA' || contentIsEditable; + var tagName = element.tagName; + var contentIsEditable = + element.contentEditable && element.contentEditable === "true"; + + // stop for input, select, and textarea + return ( + tagName === "INPUT" || + tagName === "SELECT" || + tagName === "TEXTAREA" || + contentIsEditable + ); }; // Set up key codes to event handlers function bindKeyCodesToActions(settings) { - var excludedUrl = false - if (typeof settings != "undefined") { - excludedUrl = isExcludedUrl(settings.excludedUrls, document.URL) + var excludedUrl = false; + if (typeof settings !== "undefined") { + excludedUrl = isExcludedUrl(settings.excludedUrls, document.URL); + } + // Only add if topWindow... not iframe + Mousetrap.reset(); + if (topWindow && !excludedUrl) { + Mousetrap.bind("esc", enterNormalMode); + Mousetrap.bind("ctrl+[", enterNormalMode); + Mousetrap.bind("i", enterInsertMode); + for (var actionName in actionMap) { + var keyCode = getKeyCode(actionName); + Mousetrap.bind(keyCode, executeAction(actionName), "keydown"); } - // Only add if topWindow... not iframe - Mousetrap.reset(); - if (topWindow && !excludedUrl) { - Mousetrap.bind('esc', enterNormalMode); - Mousetrap.bind('ctrl+[', enterNormalMode); - Mousetrap.bind('i', enterInsertMode); - for (var actionName in actionMap) { - if (actionMap.hasOwnProperty(actionName)) { - var keyCode = getKeyCode(actionName); - Mousetrap.bind(keyCode, executeAction(actionName), 'keydown'); - } - } - } + } } function enterNormalMode() { - // Clear input focus - document.activeElement.blur(); - - // Clear link hints (if any) - deactivateLinkHintsMode(); + // Clear input focus + document.activeElement.blur(); + // Clear link hints (if any) + deactivateLinkHintsMode(); - if (insertMode === false) { - return // We are already in normal mode. - } + if (insertMode === false) { + return; // We are already in normal mode. + } - // Re-enable if in insert mode - insertMode = false; - HUD.showForDuration('Normal Mode', hudDuration); + // Re-enable if in insert mode + insertMode = false; + HUD.showForDuration("Normal Mode", hudDuration); - Mousetrap.bind('i', enterInsertMode); + Mousetrap.bind("i", enterInsertMode); } // Calling it 'insert mode', but it's really just a user-triggered // off switch for the actions. function enterInsertMode() { - if (insertMode === true) { - return // We are already in insert mode. - } - insertMode = true; - HUD.showForDuration('Insert Mode', hudDuration); - Mousetrap.unbind('i'); + if (insertMode === true) { + return; // We are already in insert mode. + } + insertMode = true; + HUD.showForDuration("Insert Mode", hudDuration); + Mousetrap.unbind("i"); } function executeAction(actionName) { - return function() { - // don't do anything if we're not supposed to - if (linkHintsModeActivated || !extensionActive || insertMode) - return; + return function () { + // don't do anything if we're not supposed to + if (linkHintsModeActivated || !extensionActive || insertMode) return; - //Call the action function - actionMap[actionName](); + // Call the action function + actionMap[actionName](); - // Tell mousetrap to stop propagation - return false; - } + // Tell mousetrap to stop propagation + return false; + }; } +// The function is used in other files. +// eslint-disable-next-line no-unused-vars function unbindKeyCodes() { - Mousetrap.reset(); - document.removeEventListener("keydown", stopSitePropagation); + Mousetrap.reset(); + document.removeEventListener("keydown", stopSitePropagation); } // Stops propagation of keyboard events in normal mode. Adding this // callback to the document using the useCapture flag allows us to // prevent custom key behaviour implemented by the underlying website. function stopSitePropagation() { - return function (e) { - if (insertMode == false && !isActiveElementEditable()) { - e.stopPropagation() - } + return function (e) { + if (insertMode === false && !isActiveElementEditable()) { + e.stopPropagation(); } + }; } // Check whether the current active element is editable. function isActiveElementEditable() { - const el = document.activeElement; - return (el != null && isEditable(el)) + const el = document.activeElement; + return el != null && isEditable(el); } - // Adds an optional modifier to the configured key code for the action function getKeyCode(actionName) { - var keyCode = ''; - if (typeof settings != 'undefined') { - if(settings.modifier) { - keyCode += settings.modifier + '+'; - } - return keyCode + settings["bindings"][actionName]; + var keyCode = ""; + if (typeof settings !== "undefined") { + if (settings.modifier) { + keyCode += settings.modifier + "+"; } - return keyCode; -} - - -/* - * Adds the given CSS to the page. - * This function is required by vimium but depracated for vimari as the - * css is pre loaded into the page. - */ -function addCssToPage(css) { - return; + return keyCode + settings.bindings[actionName]; + } + return keyCode; } - /* * Input or text elements are considered focusable and able to receive their own keyboard events, * and will enter enter mode if focused. Also note that the "contentEditable" attribute can be set on @@ -275,90 +302,90 @@ function addCssToPage(css) { * can be controlled via the keyboard, particularly SELECT combo boxes. */ function isEditable(target) { - if (target.getAttribute("contentEditable") === "true") - return true; - var focusableInputs = ["input", "textarea", "select", "button"]; - return focusableInputs.indexOf(target.tagName.toLowerCase()) >= 0; + if (target.getAttribute("contentEditable") === "true") return true; + var focusableInputs = ["input", "textarea", "select", "button"]; + return focusableInputs.indexOf(target.tagName.toLowerCase()) >= 0; } - /* * Embedded elements like Flash and quicktime players can obtain focus but cannot be programmatically * unfocused. */ -function isEmbed(element) { return ["EMBED", "OBJECT"].indexOf(element.tagName) > 0; } - +// The function is used in other files. +// eslint-disable-next-line no-unused-vars +function isEmbed(element) { + return ["EMBED", "OBJECT"].indexOf(element.tagName) > 0; +} // ========================== // Message handling functions // ========================== -function messageHandler(event){ - if (event.name == "updateSettingsEvent") { - setSettings(event.message); - } +function messageHandler(event) { + if (event.name === "updateSettingsEvent") { + setSettings(event.message); + } } /* * Callback to pass settings to injected script */ function setSettings(msg) { - settings = msg; - activateExtension(settings); + settings = msg; + activateExtension(settings); } function activateExtension(settings) { - // Stop keydown propagation - document.addEventListener("keydown", stopSitePropagation(), true); - bindKeyCodesToActions(settings); + // Stop keydown propagation + document.addEventListener("keydown", stopSitePropagation(), true); + bindKeyCodesToActions(settings); } function isExcludedUrl(storedExcludedUrls, currentUrl) { - if (!storedExcludedUrls.length) { - return false; - } - - var excludedUrls, regexp, url, formattedUrl, _i, _len; - excludedUrls = storedExcludedUrls.split(","); - for (_i = 0, _len = excludedUrls.length; _i < _len; _i++) { - url = excludedUrls[_i]; - formattedUrl = stripProtocolAndWww(url); - formattedUrl = formattedUrl.toLowerCase().trim(); - regexp = new RegExp('((.*)?(' + formattedUrl + ')+(.*))'); - if (currentUrl.toLowerCase().match(regexp)) { - return true; - } - } + if (!storedExcludedUrls.length) { return false; + } + + var excludedUrls, regexp, url, formattedUrl, _i, _len; + excludedUrls = storedExcludedUrls.split(","); + for (_i = 0, _len = excludedUrls.length; _i < _len; _i++) { + url = excludedUrls[_i]; + formattedUrl = stripProtocolAndWww(url); + formattedUrl = formattedUrl.toLowerCase().trim(); + regexp = new RegExp("((.*)?(" + formattedUrl + ")+(.*))"); + if (currentUrl.toLowerCase().match(regexp)) { + return true; + } + } + return false; } // These formations removes the protocol and www so that // the regexp can catch less AND more specific excluded // domains than the current URL. function stripProtocolAndWww(url) { - url = url.replace('http://', ''); - url = url.replace('https://', ''); - if (url.startsWith('www.')) { - url = url.slice(4); + url = url.replace("http://", ""); + url = url.replace("https://", ""); + if (url.startsWith("www.")) { + url = url.slice(4); } return url; } // Add event listener -function inIframe () { - try { - return window.self !== window.top; - } - catch (e) { - return true; - } +function inIframe() { + try { + return window.self !== window.top; + } catch (e) { + return true; + } } -if(!inIframe()){ - extensionCommunicator.requestSettingsUpdate() +if (!inIframe()) { + extensionCommunicator.requestSettingsUpdate(); } - + // Export to make it testable window.isExcludedUrl = isExcludedUrl; window.stripProtocolAndWww = stripProtocolAndWww; diff --git a/Vimari Extension/js/keyboard-utils.js b/Vimari Extension/js/keyboard-utils.js index 20887d4..86d3ac8 100644 --- a/Vimari Extension/js/keyboard-utils.js +++ b/Vimari Extension/js/keyboard-utils.js @@ -1,4 +1,15 @@ -var keyCodes = { ESC: 27, backspace: 8, deleteKey: 46, enter: 13, space: 32, shiftKey: 16, f1: 112, f12: 123}; +/* global correctedIdentifiers:writable */ + +var keyCodes = { + ESC: 27, + backspace: 8, + deleteKey: 46, + enter: 13, + space: 32, + shiftKey: 16, + f1: 112, + f12: 123, +}; var keyNames = { 37: "left", 38: "up", 39: "right", 40: "down" }; // This is a mapping of the incorrect keyIdentifiers generated by Webkit on Windows during keydown events to @@ -15,49 +26,55 @@ var keyIdentifierCorrectionMap = { "U+00DE": ["U+0027", "U+0022"], // '" "U+00BC": ["U+002C", "U+003C"], // ,< "U+00BE": ["U+002E", "U+003E"], // .> - "U+00BF": ["U+002F", "U+003F"] // /? + "U+00BF": ["U+002F", "U+003F"], // /? }; var platform; -if (navigator.userAgent.indexOf("Mac") !== -1) - platform = "Mac"; -else if (navigator.userAgent.indexOf("Linux") !== -1) - platform = "Linux"; -else - platform = "Windows"; +if (navigator.userAgent.indexOf("Mac") !== -1) platform = "Mac"; +else if (navigator.userAgent.indexOf("Linux") !== -1) platform = "Linux"; +else platform = "Windows"; function getKeyChar(event) { - // Not a letter - if (event.keyIdentifier.slice(0, 2) !== "U+") { - // Named key - if (keyNames[event.keyCode]) { - return keyNames[event.keyCode]; - } - // F-key - if (event.keyCode >= keyCodes.f1 && event.keyCode <= keyCodes.f12) { - return "f" + (1 + event.keyCode - keyCodes.f1); - } - return ""; + // Not a letter + if (event.keyIdentifier.slice(0, 2) !== "U+") { + // Named key + if (keyNames[event.keyCode]) { + return keyNames[event.keyCode]; } - var keyIdentifier = event.keyIdentifier; - // On Windows, the keyIdentifiers for non-letter keys are incorrect. See - // https://bugs.webkit.org/show_bug.cgi?id=19906 for more details. - if ((platform === "Windows" || platform === "Linux") && keyIdentifierCorrectionMap[keyIdentifier]) { - correctedIdentifiers = keyIdentifierCorrectionMap[keyIdentifier]; - keyIdentifier = event.shiftKey ? correctedIdentifiers[0] : correctedIdentifiers[1]; + // F-key + if (event.keyCode >= keyCodes.f1 && event.keyCode <= keyCodes.f12) { + return "f" + (1 + event.keyCode - keyCodes.f1); } - var unicodeKeyInHex = "0x" + keyIdentifier.substring(2); - return String.fromCharCode(parseInt(unicodeKeyInHex)).toLowerCase(); + return ""; + } + var keyIdentifier = event.keyIdentifier; + // On Windows, the keyIdentifiers for non-letter keys are incorrect. See + // https://bugs.webkit.org/show_bug.cgi?id=19906 for more details. + if ( + (platform === "Windows" || platform === "Linux") && + keyIdentifierCorrectionMap[keyIdentifier] + ) { + correctedIdentifiers = keyIdentifierCorrectionMap[keyIdentifier]; + keyIdentifier = event.shiftKey + ? correctedIdentifiers[0] + : correctedIdentifiers[1]; + } + var unicodeKeyInHex = "0x" + keyIdentifier.substring(2); + return String.fromCharCode(parseInt(unicodeKeyInHex)).toLowerCase(); } +// The function is used in other files. +// eslint-disable-next-line no-unused-vars function isPrimaryModifierKey(event) { - if (platform === "Mac") - return event.metaKey; - else - return event.ctrlKey; + if (platform === "Mac") return event.metaKey; + else return event.ctrlKey; } +// The function is used in other files. +// eslint-disable-next-line no-unused-vars function isEscape(event) { - return event.keyCode === keyCodes.ESC || - (event.ctrlKey && getKeyChar(event) === '['); // c-[ is mapped to ESC in Vim by default. + return ( + event.keyCode === keyCodes.ESC || + (event.ctrlKey && getKeyChar(event) === "[") + ); // c-[ is mapped to ESC in Vim by default. } diff --git a/Vimari Extension/js/link-hints.js b/Vimari Extension/js/link-hints.js index cf1dc3a..f0ed890 100644 --- a/Vimari Extension/js/link-hints.js +++ b/Vimari Extension/js/link-hints.js @@ -1,3 +1,4 @@ +/* global settings, currentZoomLevel, keyCodes, getKeyChar, isEscape, safari */ /* * This implements link hinting. Typing "F" will enter link-hinting mode, where all clickable items on * the page have a hint marker displayed containing a sequence of letters. Typing those letters will select @@ -11,23 +12,19 @@ var hintMarkers = []; var hintMarkerContainingDiv = null; // The characters that were typed in while in "link hints" mode. var hintKeystrokeQueue = []; -var linkHintsModeActivated = false; var shouldOpenLinkHintInNewTab = false; var shouldOpenLinkHintWithQueue = false; -// Whether link hint's "open in current/new tab" setting is currently toggled +// Whether link hint's "open in current/new tab" setting is currently toggled var openLinkModeToggle = false; -// Whether we have added to the page the CSS needed to display link hints. -var linkHintsCssAdded = false; +var linkHintsModeActivated = false; // We need this as a top-level function because our command system doesn't yet support arguments. -function activateLinkHintsModeToOpenInNewTab() { activateLinkHintsMode(true, false); } -function activateLinkHintsModeWithQueue() { activateLinkHintsMode(true, true); } +function activateLinkHintsModeWithQueue() { + activateLinkHintsMode(true, true); +} function activateLinkHintsMode(openInNewTab, withQueue) { - if (!linkHintsCssAdded) - addCssToPage(linkHintCss); // linkHintCss is declared by vimiumFrontend.js - linkHintCssAdded = true; linkHintsModeActivated = true; setOpenLinkMode(openInNewTab, withQueue); buildLinkHints(); @@ -38,7 +35,6 @@ function activateLinkHintsMode(openInNewTab, withQueue) { function setOpenLinkMode(openInNewTab, withQueue) { shouldOpenLinkHintInNewTab = openInNewTab; shouldOpenLinkHintWithQueue = withQueue; - return; } /* @@ -49,10 +45,14 @@ function buildLinkHints() { // Initialize the number used to generate the character hints to be as many digits as we need to // highlight all the links on the page; we don't want some link hints to have more chars than others. - var digitsNeeded = Math.ceil(logXOfBase(visibleElements.length, settings.linkHintCharacters.length)); + var digitsNeeded = Math.ceil( + logXOfBase(visibleElements.length, settings.linkHintCharacters.length) + ); var linkHintNumber = 0; for (var i = 0; i < visibleElements.length; i++) { - hintMarkers.push(createMarkerFor(visibleElements[i], linkHintNumber, digitsNeeded)); + hintMarkers.push( + createMarkerFor(visibleElements[i], linkHintNumber, digitsNeeded) + ); linkHintNumber++; } // Note(philc): Append these markers as top level children instead of as child nodes to the link itself, @@ -62,12 +62,15 @@ function buildLinkHints() { hintMarkerContainingDiv = document.createElement("div"); hintMarkerContainingDiv.id = "vimiumHintMarkerContainer"; hintMarkerContainingDiv.className = "vimiumReset"; - for (var i = 0; i < hintMarkers.length; i++) - hintMarkerContainingDiv.appendChild(hintMarkers[i]); + for (var idx = 0; idx < hintMarkers.length; idx++) { + hintMarkerContainingDiv.appendChild(hintMarkers[idx]); + } document.body.appendChild(hintMarkerContainingDiv); } -function logXOfBase(x, base) { return Math.log(x) / Math.log(base); } +function logXOfBase(x, base) { + return Math.log(x) / Math.log(base); +} /* * Returns all clickable elements that are not hidden and are in the current viewport. @@ -94,44 +97,44 @@ function getVisibleClickableElements() { } function getClickableElements() { - var elements = document.getElementsByTagName('*'); + var elements = document.getElementsByTagName("*"); var clickableElements = []; for (var i = 0; i < elements.length; i++) { var element = elements[i]; - if (isClickable(element)) - clickableElements.push(element); + if (isClickable(element)) clickableElements.push(element); } return clickableElements; } function isClickable(element) { var name = element.nodeName.toLowerCase(); - var role = element.getAttribute('role'); + var role = element.getAttribute("role"); return ( // normal html elements that can be clicked - name === 'a' || - name === 'button' || - name === 'input' && element.getAttribute('type') !== 'hidden' || - name === 'select' || - name === 'textarea' || + name === "a" || + name === "button" || + (name === "input" && element.getAttribute("type") !== "hidden") || + name === "select" || + name === "textarea" || // elements having an ARIA role implying clickability // (see http://www.w3.org/TR/wai-aria/roles#widget_roles) - role === 'button' || - role === 'checkbox' || - role === 'combobox' || - role === 'link' || - role === 'menuitem' || - role === 'menuitemcheckbox' || - role === 'menuitemradio' || - role === 'radio' || - role === 'tab' || - role === 'textbox' || + role === "button" || + role === "checkbox" || + role === "combobox" || + role === "link" || + role === "menuitem" || + role === "menuitemcheckbox" || + role === "menuitemradio" || + role === "radio" || + role === "tab" || + role === "textbox" || // other ways by which we can know an element is clickable - element.hasAttribute('onclick') || - settings.detectByCursorStyle && window.getComputedStyle(element).cursor === 'pointer' && - (!element.parentNode || - window.getComputedStyle(element.parentNode).cursor !== 'pointer') + element.hasAttribute("onclick") || + (settings.detectByCursorStyle && + window.getComputedStyle(element).cursor === "pointer" && + (!element.parentNode || + window.getComputedStyle(element.parentNode).cursor !== "pointer")) ); } @@ -141,7 +144,7 @@ function isClickable(element) { * Inline elements can have more than one rect. * Block elemens only have one rect. * So, in general, add element's first visible rect, if any. - * If element does not have any visible rect, + * If element does not have any visible rect, * it can still be wrapping other visible children. * So, in that case, recurse to get the first visible rect * of the first child that has one. @@ -152,7 +155,7 @@ function getFirstVisibleRect(element) { for (var i = 0; i < clientRects.length; i++) { var clientRect = clientRects[i]; if (isVisible(element, clientRect)) { - return {element: element, rect: clientRect}; + return { element: element, rect: clientRect }; } } // Only iterate over elements with a children property. This is mainly to @@ -176,51 +179,59 @@ function getFirstVisibleRect(element) { function isVisible(element, clientRect) { // Exclude links which have just a few pixels on screen, because the link hints won't show for them anyway. var zoomFactor = currentZoomLevel / 100.0; - if (!clientRect || clientRect.top < 0 || clientRect.top * zoomFactor >= window.innerHeight - 4 || - clientRect.left < 0 || clientRect.left * zoomFactor >= window.innerWidth - 4) + if ( + !clientRect || + clientRect.top < 0 || + clientRect.top * zoomFactor >= window.innerHeight - 4 || + clientRect.left < 0 || + clientRect.left * zoomFactor >= window.innerWidth - 4 + ) { return false; + } - if (clientRect.width < 3 || clientRect.height < 3) - return false; + if (clientRect.width < 3 || clientRect.height < 3) return false; // eliminate invisible elements (see test_harnesses/visibility_test.html) var computedStyle = window.getComputedStyle(element, null); - if (computedStyle.getPropertyValue('visibility') !== 'visible' || - computedStyle.getPropertyValue('display') === 'none') + if ( + computedStyle.getPropertyValue("visibility") !== "visible" || + computedStyle.getPropertyValue("display") === "none" + ) { return false; + } // Eliminate elements hidden by another overlapping element. // To do that, get topmost element at some offset from upper-left corner of clientRect // and check whether it is the element itself or one of its descendants. // The offset is needed to account for coordinates truncation and elements with rounded borders. - // - // Coordinates truncation occcurs when using zoom. In that case, clientRect coords should be float, + // + // Coordinates truncation occcurs when using zoom. In that case, clientRect coords should be float, // but we get integers instead. That makes so that elementFromPoint(clientRect.left, clientRect.top) // sometimes returns an element different from the one clientRect was obtained from. // So we introduce an offset to make sure elementFromPoint hits the right element. // // For elements with a rounded topleft border, the upper left corner lies outside the element. // Then, we need an offset to get to the point nearest to the upper left corner, but within border. - var coordTruncationOffset = 2, // A value of 1 has been observed not to be enough, - // so we heuristically choose 2, which seems to work well. - // We know a value of 2 is still safe (lies within the element) because, - // from the code above, widht & height are >= 3. - radius = parseFloat(computedStyle.borderTopLeftRadius), - roundedBorderOffset = Math.ceil(radius * (1 - Math.sin(Math.PI / 4))), - offset = Math.max(coordTruncationOffset, roundedBorderOffset); - if (offset >= clientRect.width || offset >= clientRect.height) - return false; - var el = document.elementFromPoint(clientRect.left + offset, clientRect.top + offset); - while (el && el !== element) - el = el.parentNode; - if (!el) - return false; + var coordTruncationOffset = 2; // A value of 1 has been observed not to be enough, + // so we heuristically choose 2, which seems to work well. + // We know a value of 2 is still safe (lies within the element) because, + // from the code above, widht & height are >= 3. + var radius = parseFloat(computedStyle.borderTopLeftRadius); + var roundedBorderOffset = Math.ceil(radius * (1 - Math.sin(Math.PI / 4))); + var offset = Math.max(coordTruncationOffset, roundedBorderOffset); + if (offset >= clientRect.width || offset >= clientRect.height) return false; + var el = document.elementFromPoint( + clientRect.left + offset, + clientRect.top + offset + ); + while (el && el !== element) el = el.parentNode; + if (!el) return false; return true; } function onKeyDownInLinkHintsMode(event) { - console.log("-- key down pressed --") + console.log("-- key down pressed --"); if (event.keyCode === keyCodes.shiftKey && !openLinkModeToggle) { // Toggle whether to open link in a new or current tab. setOpenLinkMode(!shouldOpenLinkHintInNewTab, shouldOpenLinkHintWithQueue); @@ -228,13 +239,15 @@ function onKeyDownInLinkHintsMode(event) { } var keyChar = getKeyChar(event); - if (!keyChar) - return; + if (!keyChar) return; // TODO(philc): Ignore keys that have modifiers. if (isEscape(event)) { deactivateLinkHintsMode(); - } else if (event.keyCode === keyCodes.backspace || event.keyCode === keyCodes.deleteKey) { + } else if ( + event.keyCode === keyCodes.backspace || + event.keyCode === keyCodes.deleteKey + ) { if (hintKeystrokeQueue.length === 0) { deactivateLinkHintsMode(); } else { @@ -254,7 +267,7 @@ function onKeyDownInLinkHintsMode(event) { function onKeyUpInLinkHintsMode(event) { if (event.keyCode === keyCodes.shiftKey && openLinkModeToggle) { - // Revert toggle on whether to open link in new or current tab. + // Revert toggle on whether to open link in new or current tab. setOpenLinkMode(!shouldOpenLinkHintInNewTab, shouldOpenLinkHintWithQueue); openLinkModeToggle = false; } @@ -268,17 +281,23 @@ function onKeyUpInLinkHintsMode(event) { * on that link and exit link hints mode. */ function updateLinkHints() { - var hintStringLength = hintMarkers[0].getAttribute("hintString").length + var hintStringLength = hintMarkers[0].getAttribute("hintString").length; var matchString = hintKeystrokeQueue.join(""); var linksMatched = highlightLinkMatches(matchString); if (linksMatched.length === 0) { deactivateLinkHintsMode(); - } else if (linksMatched.length === 1 && matchString.length === hintStringLength) { + } else if ( + linksMatched.length === 1 && + matchString.length === hintStringLength + ) { var matchedLink = linksMatched[0]; if (isSelectable(matchedLink)) { matchedLink.focus(); // When focusing a textbox, put the selection caret at the end of the textbox's contents. - matchedLink.setSelectionRange(matchedLink.value.length, matchedLink.value.length); + matchedLink.setSelectionRange( + matchedLink.value.length, + matchedLink.value.length + ); deactivateLinkHintsMode(); } else { // When we're opening the link in the current tab, don't navigate to the selected link immediately; @@ -291,7 +310,9 @@ function updateLinkHints() { matchedLink.focus(); deactivateLinkHintsMode(); } else { - setTimeout(function() { simulateClick(matchedLink, false); }, 400); + setTimeout(function () { + simulateClick(matchedLink, false); + }, 400); matchedLink.focus(); deactivateLinkHintsMode(); } @@ -304,8 +325,11 @@ function updateLinkHints() { */ function isSelectable(element) { var selectableTypes = ["search", "text", "password"]; - return (element.tagName === "INPUT" && selectableTypes.indexOf(element.type) >= 0) || - element.tagName === "TEXTAREA"; + return ( + (element.tagName === "INPUT" && + selectableTypes.indexOf(element.type) >= 0) || + element.tagName === "TEXTAREA" + ); } /* @@ -317,10 +341,11 @@ function highlightLinkMatches(searchString) { for (var i = 0; i < hintMarkers.length; i++) { var linkMarker = hintMarkers[i]; if (linkMarker.getAttribute("hintString").indexOf(searchString) === 0) { - if (linkMarker.style.display === "none") - linkMarker.style.display = ""; - for (var j = 0; j < linkMarker.childNodes.length; j++) - linkMarker.childNodes[j].className = (j >= searchString.length) ? "" : "matchingCharacter"; + if (linkMarker.style.display === "none") linkMarker.style.display = ""; + for (var j = 0; j < linkMarker.childNodes.length; j++) { + linkMarker.childNodes[j].className = + j >= searchString.length ? "" : "matchingCharacter"; + } linksMatched.push(linkMarker.clickableItem); } else { linkMarker.style.display = "none"; @@ -346,8 +371,9 @@ function numberToHintString(number, numHintDigits) { // Pad the hint string we're returning so that it matches numHintDigits. var hintStringLength = hintString.length; - for (var i = 0; i < numHintDigits - hintStringLength; i++) + for (var i = 0; i < numHintDigits - hintStringLength; i++) { hintString.unshift(settings.linkHintCharacters[0]); + } return hintString.reverse().join(""); } @@ -365,13 +391,15 @@ function simulateClick(link, openInNewTab) { } function deactivateLinkHintsMode() { - if (hintMarkerContainingDiv) + if (hintMarkerContainingDiv) { hintMarkerContainingDiv.parentNode.removeChild(hintMarkerContainingDiv); + } hintMarkerContainingDiv = null; hintMarkers = []; hintKeystrokeQueue = []; document.removeEventListener("keydown", onKeyDownInLinkHintsMode, true); document.removeEventListener("keyup", onKeyUpInLinkHintsMode, true); + // eslint-disable-next-line no-unused-vars linkHintsModeActivated = false; } @@ -389,8 +417,11 @@ function createMarkerFor(link, linkHintNumber, linkHintDigits) { marker.className = "internalVimiumHintMarker vimiumReset"; var innerHTML = []; // Make each hint character a span, so that we can highlight the typed characters as you type them. - for (var i = 0; i < hintString.length; i++) - innerHTML.push('' + hintString[i].toUpperCase() + ''); + for (var i = 0; i < hintString.length; i++) { + innerHTML.push( + '' + hintString[i].toUpperCase() + "" + ); + } marker.innerHTML = innerHTML.join(""); marker.setAttribute("hintString", hintString); @@ -400,7 +431,7 @@ function createMarkerFor(link, linkHintNumber, linkHintDigits) { // the document node. var zoomFactor = currentZoomLevel / 100.0; marker.style.left = clientRect.left + window.scrollX / zoomFactor + "px"; - marker.style.top = clientRect.top + window.scrollY / zoomFactor + "px"; + marker.style.top = clientRect.top + window.scrollY / zoomFactor + "px"; marker.clickableItem = link.element; return marker; diff --git a/Vimari Extension/js/mocks.js b/Vimari Extension/js/mocks.js index e4e7026..cb874e7 100644 --- a/Vimari Extension/js/mocks.js +++ b/Vimari Extension/js/mocks.js @@ -1,9 +1,15 @@ var safari = { - self: { - tab: { - dispatchMessage: function () {}, - }, - addEventListener: function () {}, - } + self: { + tab: { + dispatchMessage: function () {}, + }, + addEventListener: function () {}, + }, }; window.safari = safari; + +global.SafariExtensionCommunicator = function () { + return { + requestSettingsUpdate: function () {}, + }; +}; diff --git a/Vimari Extension/json/defaultSettings.json b/Vimari Extension/json/defaultSettings.json index ef40a12..16f45cd 100644 --- a/Vimari Extension/json/defaultSettings.json +++ b/Vimari Extension/json/defaultSettings.json @@ -8,23 +8,23 @@ "smoothScroll": true, "scrollDuration": 25, "bindings": { - "hintToggle": "f", - "newTabHintToggle": "shift+f", - "scrollUp": "k", - "scrollDown": "j", - "scrollLeft": "h", - "scrollRight": "l", - "scrollUpHalfPage": "u", - "scrollDownHalfPage": "d", - "goToPageTop": "g g", - "goToPageBottom": "shift+g", - "goToFirstInput": "g i", - "goBack": "shift+h", - "goForward": "shift+l", - "reload": "r", - "tabForward": "w", - "tabBack": "q", - "closeTab": "x", - "openTab": "t" + "hintToggle": "f", + "newTabHintToggle": "shift+f", + "scrollUp": "k", + "scrollDown": "j", + "scrollLeft": "h", + "scrollRight": "l", + "scrollUpHalfPage": "u", + "scrollDownHalfPage": "d", + "goToPageTop": "g g", + "goToPageBottom": "shift+g", + "goToFirstInput": "g i", + "goBack": "shift+h", + "goForward": "shift+l", + "reload": "r", + "tabForward": "w", + "tabBack": "q", + "closeTab": "x", + "openTab": "t" } } diff --git a/Vimari/AppDelegate.swift b/Vimari/AppDelegate.swift index baaf92c..844725e 100644 --- a/Vimari/AppDelegate.swift +++ b/Vimari/AppDelegate.swift @@ -13,8 +13,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool { return true } - - @IBAction func openHelpUrl(_ sender: Any) { + + @IBAction func openHelpUrl(_: Any) { NSWorkspace.shared.open(URL(string: "https://github.com/televator-apps/vimari#usage")!) } } diff --git a/Vimari/ViewController.swift b/Vimari/ViewController.swift index 2b246b3..57ec738 100644 --- a/Vimari/ViewController.swift +++ b/Vimari/ViewController.swift @@ -1,11 +1,11 @@ import Cocoa -import SafariServices.SFSafariApplication import OSLog +import SafariServices.SFSafariApplication class ViewController: NSViewController { @IBOutlet var extensionStatus: NSTextField! @IBOutlet var spinner: NSProgressIndicator! - + private enum Constant { static let extensionIdentifier = "net.televator.Vimari.SafariExtension" static let openSettings = "openSettings" @@ -19,12 +19,10 @@ class ViewController: NSViewController { if SFSafariServicesAvailable() { SFSafariExtensionManager.getStateOfSafariExtension( - withIdentifier: Constant.extensionIdentifier) { - state, error in + withIdentifier: Constant.extensionIdentifier) { state, error in print("State", state as Any, "Error", error as Any, state?.isEnabled as Any) DispatchQueue.main.async { - // TODO: handle this getting updated in the Safari preferences too. if let state = state { if state.isEnabled { self.extensionStatus.stringValue = "Enabled" @@ -59,39 +57,41 @@ class ViewController: NSViewController { @IBAction func openSafariExtensionPreferences(_: AnyObject?) { SFSafariApplication.showPreferencesForExtension( withIdentifier: Constant.extensionIdentifier) { error in - if let _ = error { + if error != nil { // Insert code to inform the user that something went wrong. } } } - - @IBAction func openSettingsAction(_ sender: Any) { + + @IBAction func openSettingsAction(_: Any) { dispatchOpenSettings() } - - @IBAction func resetSettingsAction(_ sender: Any) { + + @IBAction func resetSettingsAction(_: Any) { dispatchResetSettings() } - + func dispatchOpenSettings() { SFSafariApplication.dispatchMessage( withName: Constant.openSettings, toExtensionWithIdentifier: Constant.extensionIdentifier, - userInfo: nil) { (error) in - if let error = error { - print(error.localizedDescription) - } + userInfo: nil + ) { error in + if let error = error { + print(error.localizedDescription) + } } } - + func dispatchResetSettings() { SFSafariApplication.dispatchMessage( withName: Constant.resetSettings, toExtensionWithIdentifier: Constant.extensionIdentifier, - userInfo: nil) { (error) in - if let error = error { - print(error.localizedDescription) - } + userInfo: nil + ) { error in + if let error = error { + print(error.localizedDescription) + } } } } diff --git a/docs/safari_12.md b/docs/safari_12.md index 8ec41ae..0e3bd29 100644 --- a/docs/safari_12.md +++ b/docs/safari_12.md @@ -4,11 +4,12 @@ A new version of macOS is being released, macOS Mojave, and it's expected to have a stable release out September or October of 2018. With that new version comes Safari 12, and a [completely new way of dealing with browser extensions](https://developer.apple.com/documentation/safariservices/safari_app_extensions). - [We have had some issues](./crowdfunding.md) related to releasing new version +[We have had some issues](./crowdfunding.md) related to releasing new version of this extension, but they are now fixed and it's possible to install a version of vimari for Safari 12. ## How to install + **Note: We are currently working on improving this installation flow, as well as the extension itself. Because vimari now has to be released as a _Safari App Extension_ instead of a _Safari Extension_ it requires some fundamental @@ -16,9 +17,9 @@ changes to the code. We can't guarantee that all the features work in this version. It's a learning process for us so bare with us.** 1. Clone this repo - ```sh - $ git clone git@github.com:guyht/vimari.git - ``` + ```sh + $ git clone git@github.com:guyht/vimari.git + ``` 2. Open the Swift project located at `/Vimari.xcodeproj` in Xcode 3. Configure the Signing settings for both the `vimari` and `extension` targets to use your information rather than the Vimari team's (see [this SO answer](https://stackoverflow.com/questions/39754341/none-of-your-accounts-are-a-member-code-signing-errors-after-upgrading-to-xcode) diff --git a/jest.config.js b/jest.config.js index 47d3b1a..1a59e11 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,8 +1,8 @@ module.exports = { setupFiles: [ "./Vimari Extension/js/mocks.js", - "./Vimari Extension/js/settings.js", + "./Vimari Extension/json/defaultSettings.json", "./Vimari Extension/js/lib/mousetrap.js", - "./Vimari Extension/js/injected.js" - ] + "./Vimari Extension/js/injected.js", + ], }; diff --git a/package-lock.json b/package-lock.json index 84af62a..67b5e78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -425,6 +425,12 @@ "@babel/types": "^7.3.0" } }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "@types/istanbul-lib-coverage": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", @@ -450,6 +456,12 @@ "@types/istanbul-lib-report": "*" } }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -501,6 +513,12 @@ } } }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, "acorn-walk": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", @@ -519,6 +537,12 @@ "uri-js": "^4.2.2" } }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, "ansi-escapes": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", @@ -550,6 +574,15 @@ "normalize-path": "^2.1.1" } }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -574,12 +607,151 @@ "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + } + } + }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + } + } + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -984,6 +1156,12 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, "convert-source-map": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", @@ -1161,6 +1339,15 @@ "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", "dev": true }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", @@ -1195,6 +1382,15 @@ "once": "^1.4.0" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -1228,28 +1424,582 @@ "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", "dev": true, "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", + "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", + "dev": true, + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "eslint": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.7.0.tgz", + "integrity": "sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.0", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^1.3.0", + "espree": "^7.2.0", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "eslint-config-standard": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz", + "integrity": "sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + } + }, + "eslint-plugin-import": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz", + "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.3", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "requires": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-promise": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", + "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", + "dev": true + }, + "eslint-plugin-standard": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", + "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" } }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true }, - "escodegen": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", - "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", + "espree": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", + "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", "dev": true, "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" + "acorn": "^7.4.0", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", + "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", + "dev": true + } } }, "esprima": { @@ -1258,6 +2008,32 @@ "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", "dev": true }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", @@ -1477,6 +2253,15 @@ "bser": "^2.0.0" } }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -1509,6 +2294,34 @@ "locate-path": "^3.0.0" } }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -2101,6 +2914,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2145,6 +2964,15 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -2279,6 +3107,30 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, "import-local": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", @@ -2418,6 +3270,12 @@ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -2430,6 +3288,15 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -2474,6 +3341,12 @@ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, "is-symbol": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", @@ -3047,6 +3920,24 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + } + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -3111,6 +4002,12 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -3521,6 +4418,18 @@ "isobject": "^3.0.0" } }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, "object.getownpropertydescriptors": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", @@ -3540,6 +4449,77 @@ "isobject": "^3.0.1" } }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + } + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3634,6 +4614,15 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -3737,6 +4726,12 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prettier": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.1.tgz", + "integrity": "sha512-9bY+5ZWCfqj3ghYBLxApy2zf6m+NJo5GzmLTpr9FsApsfjriNnS2dahWReHMi7qNPhhHl9SYHJs2cHZLgexNIw==", + "dev": true + }, "pretty-format": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", @@ -3749,6 +4744,12 @@ "react-is": "^16.8.4" } }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "prompts": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.2.1.tgz", @@ -3833,6 +4834,12 @@ "safe-regex": "^1.1.0" } }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -4098,6 +5105,17 @@ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -4287,6 +5305,12 @@ "extend-shallow": "^3.0.0" } }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -4375,6 +5399,75 @@ "strip-ansi": "^5.1.0" } }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + } + } + }, "string.prototype.trimleft": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", @@ -4395,6 +5488,75 @@ "function-bind": "^1.1.1" } }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + } + } + }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -4416,6 +5578,12 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -4431,6 +5599,18 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + } + }, "test-exclude": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", @@ -4443,6 +5623,12 @@ "require-main-filename": "^2.0.0" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "throat": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", @@ -4528,6 +5714,29 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -4552,6 +5761,12 @@ "prelude-ls": "~1.1.2" } }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, "uglify-js": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", @@ -4652,6 +5867,12 @@ "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", "dev": true }, + "v8-compile-cache": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "dev": true + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -4738,6 +5959,12 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", @@ -4761,6 +5988,15 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, "write-file-atomic": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", diff --git a/package.json b/package.json index 730fa6a..28490b7 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,14 @@ "license": "MIT", "homepage": "https://github.com/guyht/vimari", "devDependencies": { + "eslint": "^7.7.0", + "eslint-config-standard": "^14.1.1", + "eslint-plugin-import": "^2.22.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-standard": "^4.0.1", "expect.js": "^0.3.1", - "jest": "^24.9.0" + "jest": "^24.9.0", + "prettier": "2.1.1" } } diff --git a/tests/vimari.spec.js b/tests/vimari.spec.js index 3c1d7d0..0e59756 100644 --- a/tests/vimari.spec.js +++ b/tests/vimari.spec.js @@ -1,120 +1,122 @@ -const expect = require('expect.js'); - -describe('isExcludedUrl', () => { - const isExcludedUrl = window.isExcludedUrl; - - it('returns true on same exact domain', () => { - const excludedUrl = 'specific-domain.com'; - const currentUrl = excludedUrl; - expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); - }); - - it('returns true on duplicate domains', () => { - const excludedUrls = 'specific-domain.com,specific-domain.com'; - const currentUrl = 'specific-domain.com'; - expect(isExcludedUrl(excludedUrls, currentUrl)).to.be.ok(); - }); - - it('returns true if any domain match', () => { - const excludedUrls = 'different-domain.com,specific-domain.com'; - const currentUrl = 'specific-domain.com'; - expect(isExcludedUrl(excludedUrls, currentUrl)).to.be.ok(); - }); - - it('returns true on comma separated domains', () => { - const excludedUrls = 'specific-domain.com,different-domain.com'; - const currentUrl = 'specific-domain.com'; - expect(isExcludedUrl(excludedUrls, currentUrl)).to.be.ok(); - }); - - it('returns false on different domain', () => { - const excludedUrl = 'www.different-domain.com'; - const currentUrl = 'specific-domain.com'; - expect(isExcludedUrl(excludedUrl, currentUrl)).to.not.be.ok(); - }); - - it('returns false if no domains match', () => { - const excludedUrls = 'www.different-domain.com,www.different-domain-2.com'; - const currentUrl = 'specific-domain.com'; - expect(isExcludedUrl(excludedUrls, currentUrl)).to.not.be.ok(); - }); - - it('returns false on space separated domains', () => { - const excludedUrls = 'specific-domain.com different-domain.com'; - const currentUrl = 'specific-domain.com'; - expect(isExcludedUrl(excludedUrls, currentUrl)).to.not.be.ok(); - }); - - it('returns true on string added in front of current URL', () => { - const excludedUrl = 'specific-domain.com'; - const currentUrl = 'http://specific-domain.com'; - expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); - }); - - it('returns true on string appended to current URL', () => { - const excludedUrl = 'specific-domain.com'; - const currentUrl = 'specific-domain.com/arbitrary-string'; - expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); - }); - - it('returns true on string added on both sides of current URL', () => { - const excludedUrl = 'specific-domain.com'; - const currentUrl = 'http://specific-domain.com/arbitrary-string'; - expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); - }); - - it('returns true if current URL is less specific than excluded domain', () => { - let excludedUrl = 'http://specific-domain.com'; - let currentUrl = 'specific-domain.com'; - expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); - - excludedUrl = 'http://www.specific-domain.com'; - currentUrl = 'specific-domain.com'; - expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); - }); - - it('returns true if current URL with appended string is less specific than excluded domain', () => { - const excludedUrl = 'http://specific-domain.com'; - const currentUrl = 'specific-domain.com/arbitrary-string'; - expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); - }); - - it('returns true even though cases doesn\'t match', () => { - let excludedUrl = 'SPECIFIC-DOMAIN.com'; - let currentUrl = 'specific-domain.com'; - expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); - - excludedUrl = 'specific-domain.com'; - currentUrl = 'SPECIFIC-DOMAIN.com'; - expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); - }); +/* global it, describe */ + +const expect = require("expect.js"); + +describe("isExcludedUrl", () => { + const isExcludedUrl = window.isExcludedUrl; + + it("returns true on same exact domain", () => { + const excludedUrl = "specific-domain.com"; + const currentUrl = excludedUrl; + expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); + }); + + it("returns true on duplicate domains", () => { + const excludedUrls = "specific-domain.com,specific-domain.com"; + const currentUrl = "specific-domain.com"; + expect(isExcludedUrl(excludedUrls, currentUrl)).to.be.ok(); + }); + + it("returns true if any domain match", () => { + const excludedUrls = "different-domain.com,specific-domain.com"; + const currentUrl = "specific-domain.com"; + expect(isExcludedUrl(excludedUrls, currentUrl)).to.be.ok(); + }); + + it("returns true on comma separated domains", () => { + const excludedUrls = "specific-domain.com,different-domain.com"; + const currentUrl = "specific-domain.com"; + expect(isExcludedUrl(excludedUrls, currentUrl)).to.be.ok(); + }); + + it("returns false on different domain", () => { + const excludedUrl = "www.different-domain.com"; + const currentUrl = "specific-domain.com"; + expect(isExcludedUrl(excludedUrl, currentUrl)).to.not.be.ok(); + }); + + it("returns false if no domains match", () => { + const excludedUrls = "www.different-domain.com,www.different-domain-2.com"; + const currentUrl = "specific-domain.com"; + expect(isExcludedUrl(excludedUrls, currentUrl)).to.not.be.ok(); + }); + + it("returns false on space separated domains", () => { + const excludedUrls = "specific-domain.com different-domain.com"; + const currentUrl = "specific-domain.com"; + expect(isExcludedUrl(excludedUrls, currentUrl)).to.not.be.ok(); + }); + + it("returns true on string added in front of current URL", () => { + const excludedUrl = "specific-domain.com"; + const currentUrl = "http://specific-domain.com"; + expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); + }); + + it("returns true on string appended to current URL", () => { + const excludedUrl = "specific-domain.com"; + const currentUrl = "specific-domain.com/arbitrary-string"; + expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); + }); + + it("returns true on string added on both sides of current URL", () => { + const excludedUrl = "specific-domain.com"; + const currentUrl = "http://specific-domain.com/arbitrary-string"; + expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); + }); + + it("returns true if current URL is less specific than excluded domain", () => { + let excludedUrl = "http://specific-domain.com"; + let currentUrl = "specific-domain.com"; + expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); + + excludedUrl = "http://www.specific-domain.com"; + currentUrl = "specific-domain.com"; + expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); + }); + + it("returns true if current URL with appended string is less specific than excluded domain", () => { + const excludedUrl = "http://specific-domain.com"; + const currentUrl = "specific-domain.com/arbitrary-string"; + expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); + }); + + it("returns true even though cases doesn't match", () => { + let excludedUrl = "SPECIFIC-DOMAIN.com"; + let currentUrl = "specific-domain.com"; + expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); + + excludedUrl = "specific-domain.com"; + currentUrl = "SPECIFIC-DOMAIN.com"; + expect(isExcludedUrl(excludedUrl, currentUrl)).to.be.ok(); + }); }); -describe('stripProtocolAndWww', () => { - const stripProtocolAndWww = window.stripProtocolAndWww; - - it('strips http', () => { - const url = 'http://specific-domain.com'; - expect(stripProtocolAndWww(url)).to.equal('specific-domain.com'); - }); - - it('strips https', () => { - const url = 'https://specific-domain.com'; - expect(stripProtocolAndWww(url)).to.equal('specific-domain.com'); - }); - - it('strips www', () => { - const url = 'www.specific-domain.com'; - expect(stripProtocolAndWww(url)).to.equal('specific-domain.com'); - }); - - it('strips http and www', () => { - const url = 'http://www.specific-domain.com'; - expect(stripProtocolAndWww(url)).to.equal('specific-domain.com'); - }); - - it('strips https and www', () => { - const url = 'https://www.specific-domain.com'; - expect(stripProtocolAndWww(url)).to.equal('specific-domain.com'); - }); +describe("stripProtocolAndWww", () => { + const stripProtocolAndWww = window.stripProtocolAndWww; + + it("strips http", () => { + const url = "http://specific-domain.com"; + expect(stripProtocolAndWww(url)).to.equal("specific-domain.com"); + }); + + it("strips https", () => { + const url = "https://specific-domain.com"; + expect(stripProtocolAndWww(url)).to.equal("specific-domain.com"); + }); + + it("strips www", () => { + const url = "www.specific-domain.com"; + expect(stripProtocolAndWww(url)).to.equal("specific-domain.com"); + }); + + it("strips http and www", () => { + const url = "http://www.specific-domain.com"; + expect(stripProtocolAndWww(url)).to.equal("specific-domain.com"); + }); + + it("strips https and www", () => { + const url = "https://www.specific-domain.com"; + expect(stripProtocolAndWww(url)).to.equal("specific-domain.com"); + }); });