From fb1c59eb5603822fd9f1f4bce4022f82cbcfdfa7 Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Thu, 27 Aug 2020 17:28:14 +0200 Subject: [PATCH 01/16] Install SwiftFormat & SwiftLint --- .github/workflows/swift-code-checks.yml | 32 +++++++ .gitignore | 1 + .swiftformat | 1 + .swiftlint.yml | 6 ++ Package.resolved | 88 +++++++++++++++++++ Package.swift | 18 ++++ Vimari Extension/ConfigurationModel.swift | 37 ++++---- Vimari Extension/SafariExtensionHandler.swift | 61 +++++++------ Vimari/AppDelegate.swift | 4 +- Vimari/ViewController.swift | 40 ++++----- 10 files changed, 219 insertions(+), 69 deletions(-) create mode 100644 .github/workflows/swift-code-checks.yml create mode 100644 .swiftformat create mode 100644 .swiftlint.yml create mode 100644 Package.resolved create mode 100644 Package.swift diff --git a/.github/workflows/swift-code-checks.yml b/.github/workflows/swift-code-checks.yml new file mode 100644 index 0000000..fc80951 --- /dev/null +++ b/.github/workflows/swift-code-checks.yml @@ -0,0 +1,32 @@ +name: Swift Code Checks + +on: push + # pull_request: + # paths: + # - '.github/workflows/swift-code-checks.yml' + # - '.swiftformat.yml' + # - '.swiftlint.yml' + # - '**/*.swift' + +jobs: + build: + runs-on: ubuntu-latest + steps: + # Setup + - uses: actions/checkout@v1 + - 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 . + + # 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/.swiftformat b/.swiftformat new file mode 100644 index 0000000..db2d20d --- /dev/null +++ b/.swiftformat @@ -0,0 +1 @@ +--disable wrapMultilineStatementBraces \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..2e2cf5e --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,6 @@ +disabled_rules: + # Trailing comma conflicts with swift-format + - trailing_comma + +excluded: + .build/** 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..b79caf0 --- /dev/null +++ b/Package.swift @@ -0,0 +1,18 @@ +// swift-tools-version:5.3 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "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. + ] +) 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/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) + } } } } From 15b1e23c726808842cd6cb97317382c1f17bf668 Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Thu, 27 Aug 2020 17:32:06 +0200 Subject: [PATCH 02/16] Lower Swift tools version --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index b79caf0..8a3db73 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.2 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription From ba35b8d8cd5ba2928c4cf49c0e2019e4399d484d Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Thu, 27 Aug 2020 17:37:04 +0200 Subject: [PATCH 03/16] Add empty dependency --- Package.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Package.swift b/Package.swift index 8a3db73..5305a7f 100644 --- a/Package.swift +++ b/Package.swift @@ -14,5 +14,6 @@ let package = Package( 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", sources: []), ] ) From 8770033bead3306e62b9c87c39b7ecf669612338 Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Thu, 27 Aug 2020 17:42:26 +0200 Subject: [PATCH 04/16] Replace default target --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 5305a7f..9c19664 100644 --- a/Package.swift +++ b/Package.swift @@ -14,6 +14,6 @@ let package = Package( 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", sources: []), + .target(name: "Vimari", dependencies: ["SwiftLint"], path: "Vimari", sources: ["AppDelegate.Swift"]), ] ) From 08ca6f029124f25aaad2926b1abfd0e7a1d73d9d Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Thu, 27 Aug 2020 17:50:21 +0200 Subject: [PATCH 05/16] Include product for target --- Package.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 9c19664..b7cc430 100644 --- a/Package.swift +++ b/Package.swift @@ -5,6 +5,9 @@ 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"), @@ -14,6 +17,6 @@ let package = Package( 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: ["SwiftLint"], path: "Vimari", sources: ["AppDelegate.Swift"]), + .target(name: "Vimari", dependencies: [], path: "Vimari"), ] ) From db715020ecbfb5c7e5c558c4849a94cb07948211 Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Thu, 27 Aug 2020 17:59:58 +0200 Subject: [PATCH 06/16] Split format and lint step --- .github/workflows/swift-code-checks.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/swift-code-checks.yml b/.github/workflows/swift-code-checks.yml index fc80951..176f9b1 100644 --- a/.github/workflows/swift-code-checks.yml +++ b/.github/workflows/swift-code-checks.yml @@ -9,7 +9,7 @@ on: push # - '**/*.swift' jobs: - build: + format: runs-on: ubuntu-latest steps: # Setup @@ -24,7 +24,13 @@ jobs: # Run SwiftFormat in linting mode. - name: SwiftFormat run: swift run swiftformat --lint . - + + lint: + runs-on: ubuntu-latest + steps: + # Setup + - uses: actions/checkout@v1 + # Use custom SwiftLint action for GitHub inline comments. - name: SwiftLint uses: norio-nomura/action-swiftlint@3.1.0 From cf4dd4fc5d9a8bdae3c49115fc89fbb0951080ca Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Thu, 27 Aug 2020 18:01:33 +0200 Subject: [PATCH 07/16] Ignore .build for SwiftFormat --- .swiftformat | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.swiftformat b/.swiftformat index db2d20d..b8e58a4 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1 +1,2 @@ ---disable wrapMultilineStatementBraces \ No newline at end of file +--disable wrapMultilineStatementBraces +--exclude .build/ \ No newline at end of file From 0f1908915585da38bf339978901fad66636c7a06 Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Fri, 28 Aug 2020 10:54:10 +0200 Subject: [PATCH 08/16] Add Prettier formatter --- .github/ISSUE_TEMPLATE/bug_report.md | 12 +- .github/ISSUE_TEMPLATE/feature_request.md | 5 +- .github/workflows/nodejs.yml | 21 +- .github/workflows/swift-code-checks.yml | 41 +- .prettierignore | 12 + .prettierrc.json | 1 + .swiftlint.yml | 3 +- CHANGELOG.md | 84 ++-- README.md | 22 +- Vimari Extension/css/injected.css | 35 +- .../js/SafariExtensionCommunicator.js | 46 +- Vimari Extension/js/injected.js | 431 ++++++++++-------- Vimari Extension/js/keyboard-utils.js | 77 ++-- Vimari Extension/js/link-hints.js | 172 ++++--- Vimari Extension/js/mocks.js | 12 +- Vimari Extension/json/defaultSettings.json | 36 +- docs/safari_12.md | 9 +- package-lock.json | 6 + package.json | 3 +- 19 files changed, 564 insertions(+), 464 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.json 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/nodejs.yml b/.github/workflows/nodejs.yml index 8ae0675..0996c81 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -4,7 +4,6 @@ on: [push] 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@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 diff --git a/.github/workflows/swift-code-checks.yml b/.github/workflows/swift-code-checks.yml index 176f9b1..f89a332 100644 --- a/.github/workflows/swift-code-checks.yml +++ b/.github/workflows/swift-code-checks.yml @@ -1,6 +1,7 @@ name: Swift Code Checks -on: push +on: + push # pull_request: # paths: # - '.github/workflows/swift-code-checks.yml' @@ -12,27 +13,27 @@ jobs: format: runs-on: ubuntu-latest steps: - # Setup - - uses: actions/checkout@v1 - - 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 . + # Setup + - uses: actions/checkout@v1 + - 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@v1 + # Setup + - uses: actions/checkout@v1 - # Use custom SwiftLint action for GitHub inline comments. - - name: SwiftLint - uses: norio-nomura/action-swiftlint@3.1.0 - env: - DIFF_BASE: ${{ github.base_ref }} + # 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/.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/.swiftlint.yml b/.swiftlint.yml index 2e2cf5e..6eca2d5 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -2,5 +2,4 @@ disabled_rules: # Trailing comma conflicts with swift-format - trailing_comma -excluded: - .build/** +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/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/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..8292ce5 100644 --- a/Vimari Extension/js/SafariExtensionCommunicator.js +++ b/Vimari Extension/js/SafariExtensionCommunicator.js @@ -1,27 +1,27 @@ -var SafariExtensionCommunicator = (function (msgHandler) { - 'use strict' - var publicAPI = {} +var SafariExtensionCommunicator = function (msgHandler) { + "use strict"; + var publicAPI = {}; - // Connect the provided message handler to the received messages. - safari.self.addEventListener("message", msgHandler) + // Connect the provided message handler to the received messages. + safari.self.addEventListener("message", msgHandler); - var sendMessage = function(msgName) { - safari.extension.dispatchMessage(msgName) - } + var sendMessage = function (msgName) { + safari.extension.dispatchMessage(msgName); + }; - publicAPI.requestSettingsUpdate = function() { - sendMessage("updateSettings") - } - publicAPI.requestTabForward = function() { - sendMessage("tabForward") - } - publicAPI.requestTabBackward = function() { - sendMessage("tabBackward") - } - publicAPI.requestCloseTab = function () { - sendMessage("closeTab") - } + 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; -}); + // Return only the public methods. + return publicAPI; +}; diff --git a/Vimari Extension/js/injected.js b/Vimari Extension/js/injected.js index e24d1de..578b1de 100644 --- a/Vimari Extension/js/injected.js +++ b/Vimari Extension/js/injected.js @@ -6,7 +6,6 @@ * therefore we can stop certain pages (google) stealing the focus. */ - /* * Global vars * @@ -18,114 +17,141 @@ * 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 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(); }, - - '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(); } + 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 +163,141 @@ 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; } +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) { + if (actionMap.hasOwnProperty(actionName)) { + 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 input focus + document.activeElement.blur(); - // Clear link hints (if any) - deactivateLinkHintsMode(); + // 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; + }; } 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; + return keyCode + settings["bindings"][actionName]; + } + 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; } - /* * 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 +306,88 @@ 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; } - +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..b083e18 100644 --- a/Vimari Extension/js/keyboard-utils.js +++ b/Vimari Extension/js/keyboard-utils.js @@ -1,4 +1,13 @@ -var keyCodes = { ESC: 27, backspace: 8, deleteKey: 46, enter: 13, space: 32, shiftKey: 16, f1: 112, f12: 123}; +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 +24,51 @@ 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(); } function isPrimaryModifierKey(event) { - if (platform === "Mac") - return event.metaKey; - else - return event.ctrlKey; + if (platform === "Mac") return event.metaKey; + else return event.ctrlKey; } 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..8056913 100644 --- a/Vimari Extension/js/link-hints.js +++ b/Vimari Extension/js/link-hints.js @@ -14,19 +14,22 @@ 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; // We need this as a top-level function because our command system doesn't yet support arguments. -function activateLinkHintsModeToOpenInNewTab() { activateLinkHintsMode(true, false); } +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 + if (!linkHintsCssAdded) addCssToPage(linkHintCss); // linkHintCss is declared by vimiumFrontend.js linkHintCssAdded = true; linkHintsModeActivated = true; setOpenLinkMode(openInNewTab, withQueue); @@ -49,10 +52,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, @@ -67,7 +74,9 @@ function buildLinkHints() { 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 +103,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 +150,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 +161,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 +185,57 @@ 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. + 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; 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 +243,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 +271,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 +285,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 +314,9 @@ function updateLinkHints() { matchedLink.focus(); deactivateLinkHintsMode(); } else { - setTimeout(function() { simulateClick(matchedLink, false); }, 400); + setTimeout(function () { + simulateClick(matchedLink, false); + }, 400); matchedLink.focus(); deactivateLinkHintsMode(); } @@ -304,8 +329,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 +345,10 @@ 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 = ""; + 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"; + linkMarker.childNodes[j].className = + j >= searchString.length ? "" : "matchingCharacter"; linksMatched.push(linkMarker.clickableItem); } else { linkMarker.style.display = "none"; @@ -390,7 +418,9 @@ function createMarkerFor(link, linkHintNumber, linkHintDigits) { 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() + ''); + innerHTML.push( + '' + hintString[i].toUpperCase() + "" + ); marker.innerHTML = innerHTML.join(""); marker.setAttribute("hintString", hintString); @@ -400,7 +430,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..01124fb 100644 --- a/Vimari Extension/js/mocks.js +++ b/Vimari Extension/js/mocks.js @@ -1,9 +1,9 @@ var safari = { - self: { - tab: { - dispatchMessage: function () {}, - }, - addEventListener: function () {}, - } + self: { + tab: { + dispatchMessage: function () {}, + }, + addEventListener: function () {}, + }, }; window.safari = safari; 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/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/package-lock.json b/package-lock.json index 84af62a..1b856bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3737,6 +3737,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", diff --git a/package.json b/package.json index 730fa6a..76f8a4d 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "homepage": "https://github.com/guyht/vimari", "devDependencies": { "expect.js": "^0.3.1", - "jest": "^24.9.0" + "jest": "^24.9.0", + "prettier": "2.1.1" } } From ad588a35484d6c77b8a1b589a7b74950f87fda32 Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Fri, 28 Aug 2020 11:02:02 +0200 Subject: [PATCH 09/16] Add JavaScript code check workflow --- .github/workflows/js-code-checks.yml | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/js-code-checks.yml diff --git a/.github/workflows/js-code-checks.yml b/.github/workflows/js-code-checks.yml new file mode 100644 index 0000000..dbde5a0 --- /dev/null +++ b/.github/workflows/js-code-checks.yml @@ -0,0 +1,31 @@ +name: JavaScript Code Checks + +on: + push + # 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@v1 + + # Run Prettier in linting mode. + - name: Prettier + uses: creyD/prettier_action@v3.0 + with: + # Report unprettified files. + dry: true + + # lint: + # runs-on: ubuntu-latest + # steps: + # # Setup + # - uses: actions/checkout@v1 + From bf1843948f727431b2eab7dc87ea68c45f1a9ac7 Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Fri, 28 Aug 2020 11:15:04 +0200 Subject: [PATCH 10/16] Fix NodeJS workflow --- .github/workflows/nodejs.yml | 2 +- Vimari Extension/js/mocks.js | 6 + jest.config.js | 6 +- tests/vimari.spec.js | 234 +++++++++++++++++------------------ 4 files changed, 127 insertions(+), 121 deletions(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 0996c81..5573ded 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -1,6 +1,6 @@ name: Node CI -on: [push] +on: pull_request jobs: build: diff --git a/Vimari Extension/js/mocks.js b/Vimari Extension/js/mocks.js index 01124fb..e7cf29a 100644 --- a/Vimari Extension/js/mocks.js +++ b/Vimari Extension/js/mocks.js @@ -7,3 +7,9 @@ var safari = { }, }; window.safari = safari; + +global.SafariExtensionCommunicator = function () { + return { + requestSettingsUpdate: function() {} + } +} diff --git a/jest.config.js b/jest.config.js index 47d3b1a..5da002d 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/tests/vimari.spec.js b/tests/vimari.spec.js index 3c1d7d0..512350f 100644 --- a/tests/vimari.spec.js +++ b/tests/vimari.spec.js @@ -1,120 +1,120 @@ -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(); - }); +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"); + }); }); From 224e29234c184cb903919aa42ee5e84da2d3063f Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Fri, 28 Aug 2020 11:25:08 +0200 Subject: [PATCH 11/16] Upgrade to actions/checkout@v2 --- .github/workflows/js-code-checks.yml | 8 +++++--- .github/workflows/nodejs.yml | 2 +- .github/workflows/swift-code-checks.yml | 4 ++-- Vimari Extension/js/mocks.js | 8 ++++---- jest.config.js | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.github/workflows/js-code-checks.yml b/.github/workflows/js-code-checks.yml index dbde5a0..47aef7b 100644 --- a/.github/workflows/js-code-checks.yml +++ b/.github/workflows/js-code-checks.yml @@ -14,18 +14,20 @@ jobs: runs-on: ubuntu-latest steps: # Setup - - uses: actions/checkout@v1 + - 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. - dry: true + prettier_options: --check **/*.js # lint: # runs-on: ubuntu-latest # steps: # # Setup # - uses: actions/checkout@v1 - diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 5573ded..ae76b89 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -11,7 +11,7 @@ jobs: node-version: [12.x] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: diff --git a/.github/workflows/swift-code-checks.yml b/.github/workflows/swift-code-checks.yml index f89a332..6e9a494 100644 --- a/.github/workflows/swift-code-checks.yml +++ b/.github/workflows/swift-code-checks.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: # Setup - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - uses: actions/cache@v2 with: path: .build @@ -30,7 +30,7 @@ jobs: runs-on: ubuntu-latest steps: # Setup - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 # Use custom SwiftLint action for GitHub inline comments. - name: SwiftLint diff --git a/Vimari Extension/js/mocks.js b/Vimari Extension/js/mocks.js index e7cf29a..cb874e7 100644 --- a/Vimari Extension/js/mocks.js +++ b/Vimari Extension/js/mocks.js @@ -9,7 +9,7 @@ var safari = { window.safari = safari; global.SafariExtensionCommunicator = function () { - return { - requestSettingsUpdate: function() {} - } -} + return { + requestSettingsUpdate: function () {}, + }; +}; diff --git a/jest.config.js b/jest.config.js index 5da002d..1a59e11 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,7 @@ module.exports = { setupFiles: [ "./Vimari Extension/js/mocks.js", - "./Vimari Extension/json/defaultSettings.json", + "./Vimari Extension/json/defaultSettings.json", "./Vimari Extension/js/lib/mousetrap.js", "./Vimari Extension/js/injected.js", ], From 4095a65a927ea83ec298516749b88463a3ce5e5f Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Fri, 28 Aug 2020 12:22:03 +0200 Subject: [PATCH 12/16] Add ESLint --- .eslintrc.yml | 32 + .github/workflows/js-code-checks.yml | 23 +- .../js/SafariExtensionCommunicator.js | 4 + Vimari Extension/js/injected.js | 56 +- Vimari Extension/js/keyboard-utils.js | 6 + Vimari Extension/js/link-hints.js | 49 +- package-lock.json | 1262 ++++++++++++++++- package.json | 6 + tests/vimari.spec.js | 2 + 9 files changed, 1362 insertions(+), 78 deletions(-) create mode 100644 .eslintrc.yml 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/workflows/js-code-checks.yml b/.github/workflows/js-code-checks.yml index 47aef7b..cec98a4 100644 --- a/.github/workflows/js-code-checks.yml +++ b/.github/workflows/js-code-checks.yml @@ -4,10 +4,11 @@ on: push # pull_request: # paths: - # - '.github/workflows/swift-code-checks.yml' - # - '.swiftformat.yml' - # - '.swiftlint.yml' - # - '**/*.swift' + # - '.github/workflows/js-code-checks.yml' + # - '.eslintrc.yml' + # - '.prettierigore' + # - '.prettierrc.json' + # - '**/*.(js|json|css|md)' jobs: format: @@ -26,8 +27,12 @@ jobs: # Report unprettified files. prettier_options: --check **/*.js - # lint: - # runs-on: ubuntu-latest - # steps: - # # Setup - # - uses: actions/checkout@v1 + lint: + runs-on: ubuntu-latest + steps: + # Setup + - uses: actions/checkout@v2 + + # Run ESLint + - name: ESLint + uses: brandennapier/eslint-plus-action@v3.4.2 diff --git a/Vimari Extension/js/SafariExtensionCommunicator.js b/Vimari Extension/js/SafariExtensionCommunicator.js index 8292ce5..6464c7a 100644 --- a/Vimari Extension/js/SafariExtensionCommunicator.js +++ b/Vimari Extension/js/SafariExtensionCommunicator.js @@ -1,3 +1,7 @@ +/* global safari */ + +// The variable is used in other files. +// eslint-disable-next-line no-unused-vars var SafariExtensionCommunicator = function (msgHandler) { "use strict"; var publicAPI = {}; diff --git a/Vimari Extension/js/injected.js b/Vimari Extension/js/injected.js index 578b1de..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. * @@ -12,20 +14,22 @@ * 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 topWindow = window.top === window; +var settings = {}; +var extensionActive = true; +var insertMode = false; +var hudDuration = 5000; +var extensionCommunicator = SafariExtensionCommunicator(messageHandler); + +// The variables are used in other files. +/* eslint-disable no-unused-vars */ +var currentZoomLevel = 100; +var shiftKeyToggle = false; +/* eslint-enable */ var actionMap = { hintToggle: function () { @@ -163,6 +167,7 @@ function goToFirstInput() { } // Meant to be overridden, but still has to be copy/pasted from the original... +// 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+[") { @@ -195,7 +200,7 @@ Mousetrap.prototype.stopCallback = function (e, element, combo) { // Set up key codes to event handlers function bindKeyCodesToActions(settings) { var excludedUrl = false; - if (typeof settings != "undefined") { + if (typeof settings !== "undefined") { excludedUrl = isExcludedUrl(settings.excludedUrls, document.URL); } // Only add if topWindow... not iframe @@ -205,10 +210,8 @@ function bindKeyCodesToActions(settings) { 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"); - } + var keyCode = getKeyCode(actionName); + Mousetrap.bind(keyCode, executeAction(actionName), "keydown"); } } } @@ -247,7 +250,7 @@ function executeAction(actionName) { // don't do anything if we're not supposed to if (linkHintsModeActivated || !extensionActive || insertMode) return; - //Call the action function + // Call the action function actionMap[actionName](); // Tell mousetrap to stop propagation @@ -255,6 +258,8 @@ function executeAction(actionName) { }; } +// The function is used in other files. +// eslint-disable-next-line no-unused-vars function unbindKeyCodes() { Mousetrap.reset(); document.removeEventListener("keydown", stopSitePropagation); @@ -265,7 +270,7 @@ function unbindKeyCodes() { // prevent custom key behaviour implemented by the underlying website. function stopSitePropagation() { return function (e) { - if (insertMode == false && !isActiveElementEditable()) { + if (insertMode === false && !isActiveElementEditable()) { e.stopPropagation(); } }; @@ -280,24 +285,15 @@ function isActiveElementEditable() { // Adds an optional modifier to the configured key code for the action function getKeyCode(actionName) { var keyCode = ""; - if (typeof settings != "undefined") { + if (typeof settings !== "undefined") { if (settings.modifier) { keyCode += settings.modifier + "+"; } - return keyCode + settings["bindings"][actionName]; + return keyCode + settings.bindings[actionName]; } 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; -} - /* * 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 @@ -315,6 +311,8 @@ function isEditable(target) { * Embedded elements like Flash and quicktime players can obtain focus but cannot be programmatically * unfocused. */ +// The function is used in other files. +// eslint-disable-next-line no-unused-vars function isEmbed(element) { return ["EMBED", "OBJECT"].indexOf(element.tagName) > 0; } @@ -324,7 +322,7 @@ function isEmbed(element) { // ========================== function messageHandler(event) { - if (event.name == "updateSettingsEvent") { + if (event.name === "updateSettingsEvent") { setSettings(event.message); } } diff --git a/Vimari Extension/js/keyboard-utils.js b/Vimari Extension/js/keyboard-utils.js index b083e18..86d3ac8 100644 --- a/Vimari Extension/js/keyboard-utils.js +++ b/Vimari Extension/js/keyboard-utils.js @@ -1,3 +1,5 @@ +/* global correctedIdentifiers:writable */ + var keyCodes = { ESC: 27, backspace: 8, @@ -61,11 +63,15 @@ function getKeyChar(event) { 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; } +// The function is used in other files. +// eslint-disable-next-line no-unused-vars function isEscape(event) { return ( event.keyCode === keyCodes.ESC || diff --git a/Vimari Extension/js/link-hints.js b/Vimari Extension/js/link-hints.js index 8056913..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,26 +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 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 activateLinkHintsMode(openInNewTab, withQueue) { - if (!linkHintsCssAdded) addCssToPage(linkHintCss); // linkHintCss is declared by vimiumFrontend.js - linkHintCssAdded = true; linkHintsModeActivated = true; setOpenLinkMode(openInNewTab, withQueue); buildLinkHints(); @@ -41,7 +35,6 @@ function activateLinkHintsMode(openInNewTab, withQueue) { function setOpenLinkMode(openInNewTab, withQueue) { shouldOpenLinkHintInNewTab = openInNewTab; shouldOpenLinkHintWithQueue = withQueue; - return; } /* @@ -69,8 +62,9 @@ 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); } @@ -191,8 +185,9 @@ function isVisible(element, clientRect) { 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; @@ -201,8 +196,9 @@ function isVisible(element, clientRect) { 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 @@ -216,13 +212,13 @@ function isVisible(element, clientRect) { // // 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); + 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, @@ -346,9 +342,10 @@ function highlightLinkMatches(searchString) { 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++) + 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"; @@ -374,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(""); } @@ -393,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; } @@ -417,10 +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++) + for (var i = 0; i < hintString.length; i++) { innerHTML.push( '' + hintString[i].toUpperCase() + "" ); + } marker.innerHTML = innerHTML.join(""); marker.setAttribute("hintString", hintString); diff --git a/package-lock.json b/package-lock.json index 1b856bc..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", @@ -3755,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", @@ -3839,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", @@ -4104,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", @@ -4293,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", @@ -4381,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", @@ -4401,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", @@ -4422,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", @@ -4437,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", @@ -4449,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", @@ -4534,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", @@ -4558,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", @@ -4658,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", @@ -4744,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", @@ -4767,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 76f8a4d..28490b7 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,12 @@ "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", "prettier": "2.1.1" diff --git a/tests/vimari.spec.js b/tests/vimari.spec.js index 512350f..0e59756 100644 --- a/tests/vimari.spec.js +++ b/tests/vimari.spec.js @@ -1,3 +1,5 @@ +/* global it, describe */ + const expect = require("expect.js"); describe("isExcludedUrl", () => { From 8b4dd3354f8aa58bfb448bb2c76b1e0febf1bf0a Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Fri, 28 Aug 2020 12:23:03 +0200 Subject: [PATCH 13/16] Fix incorrect eslint-action repo --- .github/workflows/js-code-checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/js-code-checks.yml b/.github/workflows/js-code-checks.yml index cec98a4..acc334d 100644 --- a/.github/workflows/js-code-checks.yml +++ b/.github/workflows/js-code-checks.yml @@ -35,4 +35,4 @@ jobs: # Run ESLint - name: ESLint - uses: brandennapier/eslint-plus-action@v3.4.2 + uses: bradennapier/eslint-plus-action@v3.4.2 From cd422800ccb60502f5f8fa05a0ea318530dc76e4 Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Fri, 28 Aug 2020 12:27:13 +0200 Subject: [PATCH 14/16] Add instructions for format and linting tools --- SETUP_README.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 SETUP_README.md 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 . +``` From 37a184606e2280970e30f72d52e1edee44b42ef6 Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Fri, 28 Aug 2020 12:28:12 +0200 Subject: [PATCH 15/16] Change actions to run for only relevant PRs --- .github/workflows/js-code-checks.yml | 15 +++++++-------- .github/workflows/swift-code-checks.yml | 13 ++++++------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/js-code-checks.yml b/.github/workflows/js-code-checks.yml index acc334d..52c62f8 100644 --- a/.github/workflows/js-code-checks.yml +++ b/.github/workflows/js-code-checks.yml @@ -1,14 +1,13 @@ name: JavaScript Code Checks on: - push - # pull_request: - # paths: - # - '.github/workflows/js-code-checks.yml' - # - '.eslintrc.yml' - # - '.prettierigore' - # - '.prettierrc.json' - # - '**/*.(js|json|css|md)' + pull_request: + paths: + - '.github/workflows/js-code-checks.yml' + - '.eslintrc.yml' + - '.prettierigore' + - '.prettierrc.json' + - '**/*.(js|json|css|md)' jobs: format: diff --git a/.github/workflows/swift-code-checks.yml b/.github/workflows/swift-code-checks.yml index 6e9a494..5f15413 100644 --- a/.github/workflows/swift-code-checks.yml +++ b/.github/workflows/swift-code-checks.yml @@ -1,13 +1,12 @@ name: Swift Code Checks on: - push - # pull_request: - # paths: - # - '.github/workflows/swift-code-checks.yml' - # - '.swiftformat.yml' - # - '.swiftlint.yml' - # - '**/*.swift' + pull_request: + paths: + - '.github/workflows/swift-code-checks.yml' + - '.swiftformat.yml' + - '.swiftlint.yml' + - '**/*.swift' jobs: format: From 4f8e57a643340f99524745ad681ff5d1180ef509 Mon Sep 17 00:00:00 2001 From: Nick Belzer Date: Fri, 28 Aug 2020 12:36:58 +0200 Subject: [PATCH 16/16] Remove issue summary by eslint-plus-action --- .github/workflows/js-code-checks.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/js-code-checks.yml b/.github/workflows/js-code-checks.yml index 52c62f8..e141411 100644 --- a/.github/workflows/js-code-checks.yml +++ b/.github/workflows/js-code-checks.yml @@ -35,3 +35,5 @@ jobs: # Run ESLint - name: ESLint uses: bradennapier/eslint-plus-action@v3.4.2 + with: + issueSummary: false