mirror of
https://github.com/vale981/tridactyl
synced 2025-03-04 17:11:40 -05:00
Merge branch 'master' of github.com:cmcaine/tridactyl into glacambre-js_pipes
This commit is contained in:
commit
61a01efeab
20 changed files with 835 additions and 99 deletions
95
.appveyor.yml
Normal file
95
.appveyor.yml
Normal file
|
@ -0,0 +1,95 @@
|
|||
# Build worker image (VM template)
|
||||
image: Visual Studio 2017
|
||||
|
||||
# Build platform, i.e. x86, x64, Any CPU.
|
||||
platform:
|
||||
- Any CPU
|
||||
|
||||
# Set `git clone` directory
|
||||
clone_folder: 'C:\Tridactyl'
|
||||
|
||||
init:
|
||||
# Verify %PATH%
|
||||
- ps: Write-Host "[+] Current PATH contains ..."
|
||||
- ps: Write-Host $env:PATH.Replace(";", "`r`n")
|
||||
|
||||
# Verify Bash
|
||||
- ps: Write-Host "[+] Location of Bash ..."
|
||||
- ps: Get-Command -Name 'bash'
|
||||
|
||||
# Verify NPM
|
||||
- ps: Write-Host "[+] Location of NPM ..."
|
||||
- ps: Get-Command -Name 'npm'
|
||||
|
||||
# Verify software versions
|
||||
- ps: Write-Host "[+] Verifying software verisons ..."
|
||||
- sh --version
|
||||
- bash --version
|
||||
- node --version
|
||||
- npm --version
|
||||
|
||||
#
|
||||
# Python version will show "2.7" below, which is required to keep
|
||||
# NPM's 'bunyan' > 'dtrace-provider' modules happy.
|
||||
# 'Dtrace-provider' needs Python 'gyp' module, which is only
|
||||
# available for Python-2. We will prepend Python-3.6 to $PATH,
|
||||
# under 'build_script'.
|
||||
#
|
||||
- python --version
|
||||
|
||||
install:
|
||||
#
|
||||
# If there is a newer build queued for the same PR, cancel this
|
||||
# one. The AppVeyor 'rollout builds' option is supposed to serve
|
||||
# the same purpose but it is problematic because it tends to
|
||||
# cancel builds pushed directly to master instead of just PR
|
||||
# builds (or the converse).
|
||||
#
|
||||
# Credits: JuliaLang developers.
|
||||
#
|
||||
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER `
|
||||
-and $env:APPVEYOR_BUILD_NUMBER `
|
||||
-ne ((Invoke-RestMethod `
|
||||
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds `
|
||||
| Where-Object pullRequestId `
|
||||
-eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) `
|
||||
{ throw "Newer build in progress, giving this one up ..." }
|
||||
|
||||
# Change to build directory
|
||||
- ps: Set-Location -Path $env:APPVEYOR_BUILD_FOLDER
|
||||
|
||||
# Verify CWD
|
||||
- ps: Write-Host "[+] Current working directory is ..."
|
||||
- ps: Get-Location
|
||||
- bash -e -l -c "cd $APPVEYOR_BUILD_FOLDER && ls -alh"
|
||||
|
||||
# Install Python modules
|
||||
- ps: python -m pip install --upgrade pip
|
||||
# - ps: python -m pip install --upgrade packager
|
||||
- ps: python -m pip install --upgrade pyinstaller
|
||||
|
||||
# Install NPM modules
|
||||
# - bash -e -l -c "cd /C/Tridactyl && npm install -g windows-build-tools"
|
||||
- bash -e -l -c "cd $APPVEYOR_BUILD_FOLDER && npm install"
|
||||
|
||||
build_script:
|
||||
# Add Python-3.6 to %PATH%
|
||||
- ps: $env:PATH = "C:\Python36-x64\Scripts;$env:PATH"
|
||||
- ps: $env:PATH = "C:\Python36-x64;$env:PATH"
|
||||
- ps: Copy-Item -Path "C:\Python36-x64\Python.exe" -Destination "C:\Python36-x64\Python3.exe"
|
||||
|
||||
# Change to build directory and verify CWD
|
||||
- ps: Set-Location -Path $env:APPVEYOR_BUILD_FOLDER
|
||||
- ps: Write-Host "[+] Current working directory is ..."
|
||||
- ps: Get-Location
|
||||
|
||||
# Start build
|
||||
- ps: Write-Host "[+] Current %PATH% under Bash ..."
|
||||
- bash -e -l -c "echo $PATH"
|
||||
|
||||
- ps: Write-Host "[+] Current directory under Bash ..."
|
||||
- bash -e -l -c "cd $APPVEYOR_BUILD_FOLDER && ls -alh"
|
||||
|
||||
- ps: Write-Host "[+] Starting 'npm run build' ..."
|
||||
- bash -e -l -c "cd $APPVEYOR_BUILD_FOLDER && export PYINSTALLER=1 && npm run build"
|
||||
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -8,3 +8,8 @@ generated
|
|||
web-ext-artifacts
|
||||
yarn.lock
|
||||
.vscode/
|
||||
native/__pycache__
|
||||
native/native_main
|
||||
native_main.spec
|
||||
.wine-pyinstaller/
|
||||
tags
|
||||
|
|
51
native/gen_native_message.py
Executable file
51
native/gen_native_message.py
Executable file
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import struct
|
||||
|
||||
|
||||
def usage():
|
||||
"""Show usage and exit with non-zero status."""
|
||||
sys.stderr.write(
|
||||
"\n[+] Usage: %s cmd [command] | %s\n"
|
||||
% (os.path.basename(__file__), "native_main.py")
|
||||
)
|
||||
|
||||
sys.stderr.write("\n - Note: Use '..' as key-value separator")
|
||||
sys.stderr.write(
|
||||
"\n - Example: %s %s %s %s | %s\n"
|
||||
% (
|
||||
os.path.basename(__file__),
|
||||
"cmd..win_restart_firefox",
|
||||
"profiledir..auto",
|
||||
"browser..firefox",
|
||||
"native_main.py",
|
||||
)
|
||||
)
|
||||
|
||||
exit(-1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""Main functionalities are here for now."""
|
||||
separator = ".."
|
||||
msg = dict()
|
||||
if len(sys.argv) > 1:
|
||||
for i in range(1, len(sys.argv)):
|
||||
key = sys.argv[i].strip().split(separator)[0]
|
||||
val = sys.argv[i].strip().split(separator)[1]
|
||||
msg[key] = val
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
usage()
|
||||
|
||||
msg = json.dumps(msg)
|
||||
msg = "\r\n" + msg + "\r\n"
|
||||
msg = msg.encode("utf-8")
|
||||
packed_len = struct.pack("@I", len(msg))
|
||||
|
||||
sys.stdout.buffer.write(packed_len + msg)
|
||||
sys.stdout.flush()
|
|
@ -1,5 +1,6 @@
|
|||
Param (
|
||||
[switch]$Uninstall = $false,
|
||||
[switch]$NoPython= $false,
|
||||
[string]$DebugDirBase = "",
|
||||
[string]$InstallDirBase = ""
|
||||
)
|
||||
|
@ -8,26 +9,44 @@ Param (
|
|||
# Global constants
|
||||
#
|
||||
$global:InstallDirName = ".tridactyl"
|
||||
$global:MessengerBinName = "native_main.py"
|
||||
$global:MessengerBinPyName = "native_main.py"
|
||||
$global:MessengerBinExeName = "native_main.exe"
|
||||
$global:MessengerBinWrapperFilename = "native_main.bat"
|
||||
$global:MessengerManifestFilename = "tridactyl.json"
|
||||
$global:PythonVersionStr = "Python 3"
|
||||
$global:WinPython3Command = "py -3 -u"
|
||||
$global:MessengerManifestReplaceStr = "REPLACE_ME_WITH_SED"
|
||||
|
||||
$global:MessengerFilesHttpUriBase = [string]::Format("{0}{1}",
|
||||
"https://raw.githubusercontent.com",
|
||||
"/cmcaine/tridactyl/master/native")
|
||||
|
||||
# $git_repo_owner should be "cmcaine" in final release
|
||||
$git_repo_owner = "cmcaine"
|
||||
# $git_repo_branch should be "master" in final release
|
||||
$git_repo_branch = "cmcaine/master"
|
||||
$git_repo_proto = "https"
|
||||
$git_repo_host = "raw.githubusercontent.com"
|
||||
$git_repo_name = "tridactyl"
|
||||
$git_repo_dir = "native"
|
||||
$global:MessengerFilesHttpUriBase = `
|
||||
[string]::Format("{0}://{1}/{2}/{3}/{4}/{5}",
|
||||
$git_repo_proto,
|
||||
$git_repo_host,
|
||||
$git_repo_owner,
|
||||
$git_repo_name,
|
||||
$git_repo_branch,
|
||||
$git_repo_dir
|
||||
)
|
||||
$global:MessengerExeHttpUriBase = "https://tridactyl.cmcaine.co.uk/betas"
|
||||
$global:MessengerManifestRegistryPath = `
|
||||
"HKCU:\Software\Mozilla\NativeMessagingHosts\tridactyl"
|
||||
|
||||
$global:Uninstall = $Uninstall
|
||||
$global:NoPython= $NoPython
|
||||
$global:InstallDirBase = $InstallDirBase.Trim()
|
||||
$global:DebugDirBase = $DebugDirBase.Trim()
|
||||
|
||||
function Get-PythonVersionStatus() {
|
||||
try {
|
||||
$pythonVersion = py -3 -u --version
|
||||
$pythonVersion = Invoke-Expression `
|
||||
"$global:WinPython3Command --version"
|
||||
} catch {
|
||||
$pythonVersion = ""
|
||||
}
|
||||
|
@ -150,12 +169,22 @@ function Set-InstallDir() {
|
|||
}
|
||||
}
|
||||
|
||||
function Get-MessengerBinName() {
|
||||
$messengerBinName = $global:MessengerBinPyName
|
||||
if ($global:NoPython -eq $true) { # system doesn't have python3
|
||||
$messengerBinName = $global:MessengerBinExeName
|
||||
}
|
||||
|
||||
Return $messengerBinName
|
||||
}
|
||||
|
||||
function Get-MessengerBinPath() {
|
||||
$messengerInstallDir = Get-MessengerInstallDir
|
||||
$messengerBinName = Get-MessengerBinName
|
||||
|
||||
$native_messenger_binary_path = [string]::Format("{0}\{1}",
|
||||
$messengerInstallDir,
|
||||
$global:MessengerBinName)
|
||||
$messengerBinName)
|
||||
|
||||
Return $native_messenger_binary_path.Trim()
|
||||
}
|
||||
|
@ -165,10 +194,18 @@ function Get-MessengerBinUri() {
|
|||
-Date ((Get-Date).ToUniversalTime()) `
|
||||
-UFormat %s)
|
||||
|
||||
$messengerBinUri = [string]::Format("{0}/{1}?{2}",
|
||||
$global:MessengerFilesHttpUriBase,
|
||||
$global:MessengerBinName,
|
||||
$downloadStartTime)
|
||||
$messengerBinName = Get-MessengerBinName
|
||||
if ($global:NoPython -eq $true) { # system doesn't have python3
|
||||
$messengerBinUri = [string]::Format("{0}/{1}",
|
||||
$global:MessengerExeHttpUriBase,
|
||||
$messengerBinName
|
||||
)
|
||||
} else {
|
||||
$messengerBinUri = [string]::Format("{0}/{1}?{2}",
|
||||
$global:MessengerFilesHttpUriBase,
|
||||
$messengerBinName,
|
||||
$downloadStartTime)
|
||||
}
|
||||
|
||||
Return $messengerBinUri.Trim()
|
||||
}
|
||||
|
@ -178,15 +215,18 @@ function Set-MessengerBin() {
|
|||
$messengerBinUri = Get-MessengerBinUri
|
||||
|
||||
if ($global:DebugDirBase.Length -gt 0) {
|
||||
$messengerBinName = Get-MessengerBinName
|
||||
|
||||
$srcPath = [string]::Format("{0}\{1}",
|
||||
$global:DebugDirBase,
|
||||
$global:MessengerBinName)
|
||||
$messengerBinName)
|
||||
|
||||
Write-Host "[+] Copying $srcPath ..."
|
||||
|
||||
Copy-Item `
|
||||
-Path $srcPath `
|
||||
-Destination $messengerBinPath
|
||||
-Destination $messengerBinPath `
|
||||
-Force `
|
||||
} else {
|
||||
Write-Host "[+] Downloading $messengerBinUri ..."
|
||||
|
||||
|
@ -233,10 +273,18 @@ function Get-MessengerBinWrapperPath() {
|
|||
function Set-MessengerBinWrapper() {
|
||||
$messengerBinPath = Get-MessengerBinPath
|
||||
$messengerBinWrapperPath = Get-MessengerBinWrapperPath
|
||||
|
||||
if ($global:NoPython -eq $false) { # system has python3
|
||||
$messengerWrapperContent = @"
|
||||
@echo off
|
||||
call py -3 -u $messengerBinPath
|
||||
call $global:WinPython3Command $messengerBinPath
|
||||
"@
|
||||
} else { ## system does _not_ have python3
|
||||
$messengerWrapperContent = @"
|
||||
@echo off
|
||||
call $messengerBinPath
|
||||
"@
|
||||
}
|
||||
|
||||
Write-Host "[+] Preparing $messengerBinWrapperPath ..."
|
||||
|
||||
|
@ -298,7 +346,8 @@ function Set-MessengerManifest() {
|
|||
|
||||
Copy-Item `
|
||||
-Path $srcPath `
|
||||
-Destination $messengerManifestPath
|
||||
-Destination $messengerManifestPath `
|
||||
-Force `
|
||||
} else {
|
||||
Write-Host "[+] Downloading $messengerManifestUri ..."
|
||||
|
||||
|
@ -437,23 +486,26 @@ function Set-MessengerManifestRegistry() {
|
|||
}
|
||||
|
||||
function Set-MessengerInstall() {
|
||||
# Check for Python 3
|
||||
Write-Host "[+] Looking for Python 3 ..."
|
||||
$pythonVersionStatus = Get-PythonVersionStatus
|
||||
if (! $pythonVersionStatus) {
|
||||
Write-Host " - Python 3 not found, quitting ..."
|
||||
exit -1
|
||||
} else {
|
||||
$pythonPath = Get-Command "py" `
|
||||
# Check if system has Python 3, unless user set # the
|
||||
# `-NoPython` flag
|
||||
if ($global:NoPython -eq $false) {
|
||||
Write-Host "[+] Looking for Python 3 ..."
|
||||
$pythonVersionStatus = Get-PythonVersionStatus
|
||||
if (! $pythonVersionStatus) {
|
||||
Write-Host " - Python 3 not found, will use EXE ..."
|
||||
$global:NoPython = $true
|
||||
} else {
|
||||
$pythonPath = Get-Command "py" `
|
||||
| Select-Object -ExpandProperty "Source"
|
||||
|
||||
Write-Host " - Python 3 found at: $pythonPath"
|
||||
Write-Host " - Python 3 found at: $pythonPath"
|
||||
}
|
||||
}
|
||||
|
||||
# Prepare `.tridactyl` directory
|
||||
$result = Set-InstallDir
|
||||
|
||||
# Prepare `native_main.py`
|
||||
# Prepare `native_main.{py,exe}`
|
||||
if ($result -eq $true) {
|
||||
$result = Set-MessengerBin
|
||||
}
|
||||
|
@ -503,4 +555,3 @@ if ($global:Uninstall) {
|
|||
}
|
||||
|
||||
Set-MessengerInstall
|
||||
|
||||
|
|
39
package-lock.json
generated
39
package-lock.json
generated
|
@ -2245,7 +2245,8 @@
|
|||
"discontinuous-range": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
|
||||
"integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo="
|
||||
"integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=",
|
||||
"dev": true
|
||||
},
|
||||
"dispensary": {
|
||||
"version": "0.10.10",
|
||||
|
@ -5781,9 +5782,9 @@
|
|||
"integrity": "sha1-GA8fnr74sOY45BZq1S24eb6y/8U="
|
||||
},
|
||||
"marked": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz",
|
||||
"integrity": "sha1-ssbGGPzOzk74bE/Gy4p8v1rtqNc=",
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-0.4.0.tgz",
|
||||
"integrity": "sha512-tMsdNBgOsrUophCAFQl0XPe6Zqk/uy9gnue+jIIKhykO51hxyu6uNx7zBPy0+y/WKYVZZMspV9YeXLNdKk+iYw==",
|
||||
"dev": true
|
||||
},
|
||||
"md5.js": {
|
||||
|
@ -6096,13 +6097,15 @@
|
|||
"optional": true
|
||||
},
|
||||
"nearley": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/nearley/-/nearley-2.11.0.tgz",
|
||||
"integrity": "sha512-clqqhEuP0ZCJQ85Xv2I/4o2Gs/fvSR6fCg5ZHVE2c8evWyNk2G++ih4JOO3lMb/k/09x6ihQ2nzKUlB/APCWjg==",
|
||||
"version": "2.13.0",
|
||||
"resolved": "https://registry.npmjs.org/nearley/-/nearley-2.13.0.tgz",
|
||||
"integrity": "sha512-ioYYogSaZhFlCpRizQgY3UT3G1qFXmHGY/5ozoFE3dMfiCRAeJfh+IPE3/eh9gCZvqLhPCWb4bLt7Bqzo+1mLQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nomnom": "~1.6.2",
|
||||
"railroad-diagrams": "^1.0.0",
|
||||
"randexp": "^0.4.2"
|
||||
"randexp": "0.4.6",
|
||||
"semver": "^5.4.1"
|
||||
}
|
||||
},
|
||||
"node-dir": {
|
||||
|
@ -6193,6 +6196,7 @@
|
|||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz",
|
||||
"integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"colors": "0.5.x",
|
||||
"underscore": "~1.4.4"
|
||||
|
@ -6201,7 +6205,8 @@
|
|||
"colors": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz",
|
||||
"integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q="
|
||||
"integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -6941,12 +6946,14 @@
|
|||
"railroad-diagrams": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
|
||||
"integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234="
|
||||
"integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=",
|
||||
"dev": true
|
||||
},
|
||||
"randexp": {
|
||||
"version": "0.4.6",
|
||||
"resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
|
||||
"integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"discontinuous-range": "1.0.0",
|
||||
"ret": "~0.1.10"
|
||||
|
@ -7300,7 +7307,8 @@
|
|||
"ret": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
|
||||
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
|
||||
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
|
||||
"dev": true
|
||||
},
|
||||
"right-align": {
|
||||
"version": "0.1.3",
|
||||
|
@ -8786,6 +8794,12 @@
|
|||
"typescript": "2.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"marked": {
|
||||
"version": "0.3.19",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
|
||||
"integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==",
|
||||
"dev": true
|
||||
},
|
||||
"progress": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
|
||||
|
@ -8855,7 +8869,8 @@
|
|||
"underscore": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
|
||||
"integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ="
|
||||
"integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=",
|
||||
"dev": true
|
||||
},
|
||||
"union-value": {
|
||||
"version": "1.0.0",
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
"css": "^2.2.1",
|
||||
"fuse.js": "^3.2.0",
|
||||
"mark.js": "^8.11.1",
|
||||
"nearley": "^2.11.0",
|
||||
"semver-compare": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -20,6 +19,8 @@
|
|||
"cleanslate": "^0.10.1",
|
||||
"copy-webpack-plugin": "^4.2.0",
|
||||
"jest": "^21.2.1",
|
||||
"marked": "^0.4.0",
|
||||
"nearley": "^2.13.0",
|
||||
"prettier": "^1.11.1",
|
||||
"shared-git-hooks": "^1.2.1",
|
||||
"source-map-loader": "^0.2.2",
|
||||
|
|
65
readme.md
65
readme.md
|
@ -95,7 +95,7 @@ Extended hint modes allow you to perform actions on page items:
|
|||
* `;k` — delete an element from the page
|
||||
* `;;` — focus an element
|
||||
|
||||
Additionally, you can bind to a custom CSS selector with `:hint -c [selector]` which is useful for site-specific versions of the standard `f` hint mode.
|
||||
Additionally, you can hint elements matching a custom CSS selector with `:hint -c [selector]` which is useful for site-specific versions of the standard `f` hint mode.
|
||||
|
||||
### Binding custom commands
|
||||
|
||||
|
@ -230,7 +230,7 @@ $(npm bin)/web-ext build -s build
|
|||
|
||||
If you want to build a signed copy (e.g. for the non-developer release), you can do that with `web-ext sign`. You'll need some keys for AMO and to edit the application id in `src/manifest.json`. There's a helper script in `scripts/sign` that's used by our build bot and for manual releases.
|
||||
|
||||
#### Building on Windows
|
||||
### Building on Windows
|
||||
|
||||
* Install [Git for Windows][win-git]
|
||||
|
||||
|
@ -243,6 +243,67 @@ If you want to build a signed copy (e.g. for the non-developer release), you can
|
|||
|
||||
[win-git]: https://git-scm.com/download/win
|
||||
[win-nodejs]: https://nodejs.org/dist/v8.11.1/node-v8.11.1-x64.msi
|
||||
[pyinstaller]: https://www.pyinstaller.org
|
||||
[gpg4win]: https://www.gpg4win.org
|
||||
|
||||
<!--- ## Disable GPG signing for now, until decided otherwise later
|
||||
|
||||
### Cryptographically Verifying the Compiled Native Binary on Windows
|
||||
|
||||
- `native_main.py` is compiled to `native_main.exe` for Windows using [PyInstaller][pyinstaller]. The goal is to relieve Tridactyl users on Windows from having to install the whole Python 3 distribution.
|
||||
|
||||
- Due to `native_main.exe` being a binary-blob and difficult to easily review like the plain-text `native_main.py` counterpart, it is **strongly** recommended the users verify the SHA-256 hash and GPG signatures using the following commands on Powershell.
|
||||
|
||||
**Verifying SHA-256 Hash**
|
||||
|
||||
```
|
||||
## Change directory to Tridactyl's native-messanger directory
|
||||
PS C:\> cd "$env:HOME\.tridactyl"
|
||||
|
||||
## Run `dir` and check `native_main.exe` is found
|
||||
PS C:\Users\{USERNAME}\.tridactyl> dir
|
||||
|
||||
## Download `native_main.exe.sha256` containing the SHA-256 sum
|
||||
PS C:\Users\{USERNAME}\.tridactyl> iwr https://raw.githubusercontent.com/gsbabil/tridactyl/master/native/native_main.exe.sha256 -OutFile native_main.exe.sha256
|
||||
|
||||
## Print the SHA-256 sum from `native_main.exe.sha256`
|
||||
PS C:\Users\{USERNAME}\.tridactyl> (Get-FileHash native_main.exe -Algorithm SHA256).Hash.ToLower()
|
||||
|
||||
## Compute SHA-256 sum from `native_main.exe`
|
||||
PS C:\Users\{USERNAME}\.tridactyl> Get-Content -Path native_main.exe.sha256 | %{$_ .Split(' ')[0]}
|
||||
|
||||
## Compare results of the of the last two commands ...
|
||||
```
|
||||
|
||||
**Verifying OpenPGP Signature***
|
||||
|
||||
- First, download [`GPG4Win`][gpg4win] from this website and
|
||||
install in on your system
|
||||
|
||||
- Once `gpg2` is on your path, go to Powershell and run the
|
||||
following commands to verify OpenPGP signature:
|
||||
|
||||
```
|
||||
## Change directory to Tridactyl's native-messanger directory
|
||||
PS C:\> cd "$env:HOME\.tridactyl"
|
||||
|
||||
## Run `dir` and check `native_main.exe` is found
|
||||
PS C:\Users\{USERNAME}\.tridactyl> dir
|
||||
|
||||
## Download `native_main.exe.sig` containing the OpenPGP signature
|
||||
PS C:\Users\{USERNAME}\.tridactyl> iwr https://raw.githubusercontent.com/gsbabil/tridactyl/master/native/native_main.exe.sig -OutFile native_main.exe.sig
|
||||
|
||||
## Download `gsbabil-pub.asc` to verify the signature
|
||||
PS C:\Users\{USERNAME}\.tridactyl> iwr https://raw.githubusercontent.com/gsbabil/tridactyl/master/native/gsbabil-pub.asc -OutFile gsbabil-pub.asc
|
||||
|
||||
## Import `gsbabil-pub.asc` into your GPG key-ring
|
||||
PS C:\Users\{USERNAME}\.tridactyl> gpg2 --armor --import gsbabil-pub.asc
|
||||
|
||||
## Verify signature using `gpg2`
|
||||
PS C:\Users\{USERNAME}\.tridactyl> gpg2 --verify .\native_main.exe.sig .\native_main.exe
|
||||
```
|
||||
|
||||
--->
|
||||
|
||||
### Development loop
|
||||
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
# Put the AMO flavour text in your clipboard for easy pasting.
|
||||
# AMO doesn't support all HTML in markdown so we strip it out.
|
||||
|
||||
marked doc/amo.md | sed -r "s/<.?p>//g" | sed -r "s/<.?h.*>//g" | xclip -selection "clipboard"
|
||||
$(npm bin)/marked doc/amo.md | sed -r "s/<.?p>//g" | sed -r "s/<.?h.*>//g" | xclip -selection "clipboard"
|
||||
|
|
|
@ -6,7 +6,8 @@ CLEANSLATE="node_modules/cleanslate/docs/files/cleanslate.css"
|
|||
|
||||
isWindowsMinGW() {
|
||||
local is_mingw="False"
|
||||
if [ "$(uname | cut -c 1-5)" = "MINGW" ]; then
|
||||
if [ "$(uname | cut -c 1-5)" = "MINGW" ] \
|
||||
|| [ "$(uname | cut -c 1-4)" = "MSYS" ]; then
|
||||
is_mingw="True"
|
||||
fi
|
||||
|
||||
|
@ -37,7 +38,7 @@ scripts/newtab.md.sh
|
|||
scripts/make_tutorial.sh
|
||||
scripts/make_docs.sh &
|
||||
|
||||
nearleyc src/grammars/bracketexpr.ne \
|
||||
$(npm bin)/nearleyc src/grammars/bracketexpr.ne \
|
||||
> src/grammars/.bracketexpr.generated.ts
|
||||
|
||||
if [ "$(isWindowsMinGW)" = "True" ]; then
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
dest=generated/static/docs
|
||||
typedoc --theme src/static/typedoc/ --out $dest src --ignoreCompilerErrors
|
||||
$(npm bin)/typedoc --theme src/static/typedoc/ --out $dest src --ignoreCompilerErrors
|
||||
cp -r $dest build/static/
|
||||
|
|
|
@ -11,6 +11,6 @@ for page in $pages
|
|||
do
|
||||
fileroot=$(echo $page | cut -d'.' -f-1)
|
||||
sed "/REPLACETHIS/,$ d" tutor.template.html > "$dest$fileroot.html"
|
||||
marked $page >> "$dest$fileroot.html"
|
||||
$(npm bin)/marked $page >> "$dest$fileroot.html"
|
||||
sed "1,/REPLACETHIS/ d" tutor.template.html >> "$dest$fileroot.html"
|
||||
done
|
||||
|
|
|
@ -8,7 +8,7 @@ newtab="../../generated/static/newtab.html"
|
|||
newtabtemp="../../generated/static/newtab.temp.html"
|
||||
|
||||
sed "/REPLACETHIS/,$ d" newtab.template.html > "$newtabtemp"
|
||||
marked newtab.md >> "$newtabtemp"
|
||||
$(npm bin)/marked newtab.md >> "$newtabtemp"
|
||||
sed "1,/REPLACETHIS/ d" newtab.template.html >> "$newtabtemp"
|
||||
|
||||
# Why think when you can pattern match?
|
||||
|
@ -19,7 +19,7 @@ echo """
|
|||
<label for="spoilerbutton" onclick="">Changelog</label>
|
||||
<div class="spoiler">
|
||||
""" >> "$newtab"
|
||||
marked ../../CHANGELOG.md >> "$newtab"
|
||||
$(npm bin)/marked ../../CHANGELOG.md >> "$newtab"
|
||||
echo """
|
||||
</div>
|
||||
""" >> "$newtab"
|
||||
|
|
167
scripts/wine-pyinstaller.sh
Executable file
167
scripts/wine-pyinstaller.sh
Executable file
|
@ -0,0 +1,167 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
# This script must be run from the root Tridactyl directory
|
||||
TRIDIR="$(pwd)"
|
||||
|
||||
BUILDROOT="${TRIDIR}/.wine-pyinstaller"
|
||||
OUTDIR="${BUILDROOT}/dist"
|
||||
DLDIR="${BUILDROOT}/downloads"
|
||||
|
||||
PYVER="3.5.4"
|
||||
PYDIR="${BUILDROOT}/python-${PYVER}"
|
||||
WINPY_HASH="b5d90c5252a624117ccec8678862d6144710219737f06cd01deb1df963f639fd"
|
||||
WINPY_EXE="${DLDIR}/winpython-${PYVER}.exe"
|
||||
|
||||
WINEDIR="${BUILDROOT}/wine"
|
||||
WINEARCH="win32"
|
||||
export WINEDEBUG="fixme-all"
|
||||
|
||||
# stop wine whining
|
||||
export DISPLAY=
|
||||
|
||||
PREREQUISITES="tput printf 7z wine"
|
||||
|
||||
MIN_WINE_VER="3"
|
||||
MIN_7ZIP_VER="16"
|
||||
|
||||
checkRequiredVersions() {
|
||||
if [ -z "$(7z \
|
||||
| awk '/Version/{print $3}' \
|
||||
| grep "${MIN_7ZIP_VER}")" ]; then
|
||||
colorEcho \
|
||||
"[-] p7zip minimum version ${MIN_7ZIP_VER} required\n" \
|
||||
"alert"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
if [ -z "$(wine --version 2> /dev/null \
|
||||
| grep "wine-${MIN_WINE_VER}")" ]; then
|
||||
colorEcho \
|
||||
"[-] wine minimum version ${MIN_WINE_VER} required\n" \
|
||||
"alert"
|
||||
exit -1
|
||||
fi
|
||||
}
|
||||
|
||||
stripWhitespace() {
|
||||
local input="$@"
|
||||
printf "${input}\n" | tr -d "[:space:]"
|
||||
}
|
||||
|
||||
colorEcho() {
|
||||
local COLOR_RESET=$(tput sgr0 2>/dev/null)
|
||||
local COLOR_BOLD=$(tput bold 2>/dev/null)
|
||||
local COLOR_BAD=$(tput setaf 1 2>/dev/null)
|
||||
local COLOR_GOOD=$(tput setaf 2 2>/dev/null)
|
||||
|
||||
local str="$1"
|
||||
local color="${COLOR_GOOD}${COLOR_BOLD}"
|
||||
|
||||
if [ ! -z "$2" ] \
|
||||
&& [ "$(stripWhitespace "$2")" = "alert" ]; then
|
||||
color="${COLOR_BAD}${COLOR_BOLD}"
|
||||
fi
|
||||
|
||||
printf "${color}${str}${COLOR_RESET}"
|
||||
}
|
||||
|
||||
checkPrerequisite() {
|
||||
local bin_name="$1"
|
||||
local bin_loc=$(which "${bin_name}" 2>/dev/null)
|
||||
|
||||
if [ -z "${bin_loc}" ] \
|
||||
|| [ ! -f "${bin_loc}" ]; then
|
||||
printf " - '$1' not found, quitting ...\n"
|
||||
exit -1
|
||||
else
|
||||
printf " - '${bin_name}' found at ${bin_loc}\n"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
mainFunction() {
|
||||
export "WINEARCH=${WINEARCH}"
|
||||
export "WINEPREFIX=${WINEDIR}"
|
||||
|
||||
|
||||
## Check prerequisites
|
||||
colorEcho "[+] Checking prerequisites ...\n"
|
||||
for bin in ${PREREQUISITES}; do
|
||||
checkPrerequisite "${bin}"
|
||||
done
|
||||
|
||||
checkRequiredVersions
|
||||
|
||||
|
||||
## Create required directories
|
||||
mkdir -pv "${BUILDROOT}"
|
||||
mkdir -pv "${DLDIR}"
|
||||
mkdir -pv "${OUTDIR}"
|
||||
mkdir -pv "$TRIDIR"/web-ext-artifacts/
|
||||
|
||||
|
||||
## Download Python and Pip
|
||||
colorEcho "[+] Downloading necessary files ...\n"
|
||||
|
||||
if [ ! -f "${WINPY_EXE}" ]; then
|
||||
wget \
|
||||
"https://github.com/winpython/winpython/releases/download/1.10.20180404/WinPython32-${PYVER}.2Zero.exe" \
|
||||
-O "${WINPY_EXE}"
|
||||
fi
|
||||
|
||||
if [ ! "$(sha256sum "${WINPY_EXE}" \
|
||||
| cut -d" " -f1)" = ${WINPY_HASH} ]; then
|
||||
colorEcho "[-] ${WINPY_EXE} has incorrect hash, quitting ...\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
## Extract Python-3.5.4 from WinPython if required
|
||||
|
||||
rm -rf "${WINEDIR}"
|
||||
local winepython="wine $PYDIR/python.exe"
|
||||
|
||||
if [ ! -f "$PYDIR/python.exe" ]; then
|
||||
colorEcho "[+] Extract Python-${PYVER}\n"
|
||||
7z x "${DLDIR}/winpython-${PYVER}.exe" "python-$PYVER" -o"$BUILDROOT"
|
||||
|
||||
$winepython -m pip install --upgrade pip
|
||||
|
||||
colorEcho "[+] Installing PyInstaller ...\n"
|
||||
$winepython -m pip install pyinstaller
|
||||
fi
|
||||
|
||||
## Compile with PyInstaller
|
||||
colorEcho "[+] Compiling with PyInstaller under Wine ...\n"
|
||||
rm -rf "${OUTDIR}"
|
||||
PYTHONHASHSEED=1 wine "$PYDIR"/Scripts/pyinstaller.exe \
|
||||
--clean \
|
||||
--console \
|
||||
--onefile \
|
||||
--noupx \
|
||||
--noconfir \
|
||||
--log-level=ERROR \
|
||||
--workpath "${OUTDIR}" \
|
||||
--distpath "${OUTDIR}" \
|
||||
"$TRIDIR/native/native_main.py"
|
||||
|
||||
## Test the compiled EXE
|
||||
colorEcho "[+] Checking compiled binary ...\n"
|
||||
OUTFILE="${OUTDIR}/native_main.exe"
|
||||
cp "$OUTFILE" "$TRIDIR"/web-ext-artifacts/
|
||||
|
||||
if [ -f "${OUTFILE}" ]; then
|
||||
python3 \
|
||||
"$TRIDIR/native/gen_native_message.py" cmd..version \
|
||||
| wine "$TRIDIR"/web-ext-artifacts/native_main.exe
|
||||
|
||||
printf "\n"
|
||||
colorEcho "[+] PyInstaller with Wine was successful!\n"
|
||||
else
|
||||
colorEcho \
|
||||
"[-] PyInstaller compilation failed, quitting ...\n" \
|
||||
"alert"
|
||||
exit -1
|
||||
fi
|
||||
}
|
||||
|
||||
mainFunction "$@"
|
|
@ -73,8 +73,8 @@ const DEFAULTS = o({
|
|||
"<c-6>": "buffer #",
|
||||
H: "back",
|
||||
L: "forward",
|
||||
"<c-o>": "back",
|
||||
"<c-i>": "forward",
|
||||
"<c-o>": "jumpprev",
|
||||
"<c-i>": "jumpnext",
|
||||
d: "tabclose",
|
||||
D: "composite tabprev; sleep 100; tabclose #",
|
||||
gx0: "tabclosealltoleft",
|
||||
|
@ -233,6 +233,11 @@ const DEFAULTS = o({
|
|||
// Maybe have a nice user-vicible message when the setting is changed?
|
||||
allowautofocus: "true",
|
||||
|
||||
// These two options will fall back to user's preferences and then to a
|
||||
// default value set in scrolling.ts if left undefined.
|
||||
smoothscroll: undefined, // "false" | "true"
|
||||
scrollduration: undefined, // number
|
||||
|
||||
tabopenpos: "next",
|
||||
relatedopenpos: "related",
|
||||
ttsvoice: "default", // chosen from the listvoices list, or "default"
|
||||
|
@ -252,6 +257,8 @@ const DEFAULTS = o({
|
|||
theme: "default", // currently available: "default", "dark"
|
||||
modeindicator: "true",
|
||||
|
||||
jumpdelay: 3000, // Milliseconds before registering a scroll in the jumplist
|
||||
|
||||
// Default logging levels - 2 === WARNING
|
||||
logging: o({
|
||||
messaging: 2,
|
||||
|
@ -274,9 +281,9 @@ const DEFAULTS = o({
|
|||
nativeinstallcmd:
|
||||
"curl -fsSl https://raw.githubusercontent.com/cmcaine/tridactyl/master/native/install.sh | bash",
|
||||
win_powershell_nativeinstallcmd:
|
||||
"Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/gsbabil/tridactyl/master/native/win_install.ps1'))",
|
||||
"Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/cmcaine/tridactyl/master/native/win_install.ps1'))",
|
||||
win_cmdexe_nativeinstallcmd:
|
||||
'@"%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" -NoProfile -InputFormat None -Command "iex ((New-Object System.Net.WebClient).DownloadString(\'https://raw.githubusercontent.com/gsbabil/tridactyl/master/native/win_install.ps1\'))"',
|
||||
'@"%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" -NoProfile -InputFormat None -Command "iex ((New-Object System.Net.WebClient).DownloadString(\'https://raw.githubusercontent.com/cmcaine/tridactyl/master/native/win_install.ps1\'))"',
|
||||
profiledir: "auto",
|
||||
|
||||
// Container settings
|
||||
|
|
154
src/excmds.ts
154
src/excmds.ts
|
@ -105,6 +105,7 @@ import * as SELF from "./.excmds_content.generated"
|
|||
Messaging.addListener("excmd_content", Messaging.attributeCaller(SELF))
|
||||
import * as DOM from "./dom"
|
||||
import { executeWithoutCommandLine } from "./commandline_content"
|
||||
import * as scrolling from "./scrolling"
|
||||
// }
|
||||
|
||||
//#background_helper
|
||||
|
@ -514,6 +515,110 @@ export function loggingsetlevel(logModule: string, level: string) {
|
|||
|
||||
// {{{ PAGE CONTEXT
|
||||
|
||||
//#content_helper
|
||||
export let JUMPED: boolean
|
||||
|
||||
//#content_helper
|
||||
export function getJumpPageId() {
|
||||
return document.location.href
|
||||
}
|
||||
|
||||
//#content_helper
|
||||
export async function saveJumps(jumps) {
|
||||
browserBg.sessions.setTabValue(await activeTabId(), "jumps", jumps)
|
||||
}
|
||||
|
||||
//#content_helper
|
||||
export async function curJumps() {
|
||||
let tabid = await activeTabId()
|
||||
let jumps = await browserBg.sessions.getTabValue(tabid, "jumps")
|
||||
if (!jumps) jumps = {}
|
||||
let ensure = (obj, key, def) => {
|
||||
if (obj[key] === null || obj[key] === undefined) obj[key] = def
|
||||
}
|
||||
let page = getJumpPageId()
|
||||
ensure(jumps, page, {})
|
||||
ensure(jumps[page], "list", [{ x: 0, y: 0 }])
|
||||
ensure(jumps[page], "cur", 0)
|
||||
saveJumps(jumps)
|
||||
return jumps
|
||||
}
|
||||
|
||||
//#content
|
||||
export function jumpnext(n = 1) {
|
||||
jumpprev(-n)
|
||||
}
|
||||
|
||||
/** Similar to Pentadactyl or vim's jump list.
|
||||
Should be bound to <C-o> when modifiers are implemented
|
||||
*/
|
||||
//#content
|
||||
export function jumpprev(n = 1) {
|
||||
curJumps().then(alljumps => {
|
||||
let jumps = alljumps[getJumpPageId()]
|
||||
let current = jumps.cur - n
|
||||
if (current < 0) {
|
||||
jumps.cur = 0
|
||||
saveJumps(alljumps)
|
||||
return back(-current)
|
||||
} else if (current >= jumps.list.length) {
|
||||
jumps.cur = jumps.list.length - 1
|
||||
saveJumps(alljumps)
|
||||
return forward(current - jumps.list.length + 1)
|
||||
}
|
||||
jumps.cur = current
|
||||
let p = jumps.list[jumps.cur]
|
||||
saveJumps(alljumps)
|
||||
JUMPED = true
|
||||
window.scrollTo(p.x, p.y)
|
||||
})
|
||||
}
|
||||
|
||||
/** Called on 'scroll' events.
|
||||
If you want to have a function that moves within the page but doesn't add a
|
||||
location to the jumplist, make sure to set JUMPED to true before moving
|
||||
around.
|
||||
The setTimeout call is required because sometimes a user wants to move
|
||||
somewhere by pressing 'j' multiple times and we don't want to add the
|
||||
in-between locations to the jump list
|
||||
*/
|
||||
//#content_helper
|
||||
export function addJump(scrollEvent: UIEvent) {
|
||||
if (JUMPED) {
|
||||
JUMPED = false
|
||||
return
|
||||
}
|
||||
let pageX = scrollEvent.pageX
|
||||
let pageY = scrollEvent.pageY
|
||||
// Get config for current page
|
||||
curJumps().then(alljumps => {
|
||||
let jumps = alljumps[getJumpPageId()]
|
||||
// Prevent pending jump from being registered
|
||||
clearTimeout(jumps.timeoutid)
|
||||
// Schedule the registering of the current jump
|
||||
jumps.timeoutid = setTimeout(() => {
|
||||
let list = jumps.list
|
||||
// if the page hasn't moved, stop
|
||||
if (list[jumps.cur].x == pageX && list[jumps.cur].y == pageY) return
|
||||
// Store the new jump
|
||||
// Could removing all jumps from list[cur] to list[list.length] be
|
||||
// a better/more intuitive behavior?
|
||||
list.push({ x: pageX, y: pageY })
|
||||
jumps.cur = jumps.list.length - 1
|
||||
saveJumps(alljumps)
|
||||
}, config.get("jumpdelay"))
|
||||
})
|
||||
}
|
||||
|
||||
//#content_helper
|
||||
document.addEventListener("scroll", addJump)
|
||||
|
||||
// Try to restore the previous jump position every time a page is loaded
|
||||
//#content_helper
|
||||
curJumps().then(() => {
|
||||
jumpprev(0)
|
||||
})
|
||||
|
||||
/** Blur (unfocus) the active element */
|
||||
//#content
|
||||
export function unfocus() {
|
||||
|
@ -522,10 +627,8 @@ export function unfocus() {
|
|||
}
|
||||
|
||||
//#content
|
||||
export function scrollpx(a: number, b: number) {
|
||||
let top = document.body.getClientRects()[0].top
|
||||
window.scrollBy(a, b)
|
||||
if (top == document.body.getClientRects()[0].top) recursiveScroll(a, b, [document.body])
|
||||
export async function scrollpx(a: number, b: number) {
|
||||
if (!await scrolling.scroll(a, b, document.documentElement)) scrolling.recursiveScroll(a, b, [document.documentElement])
|
||||
}
|
||||
|
||||
/** If two numbers are given, treat as x and y values to give to window.scrollTo
|
||||
|
@ -542,54 +645,27 @@ export function scrollto(a: number, b: number | "x" | "y" = "y") {
|
|||
window.scrollTo(window.scrollX, percentage * elem.scrollHeight / 100)
|
||||
if (top == elem.getClientRects()[0].top && (percentage == 0 || percentage == 100)) {
|
||||
// scrollTo failed, if the user wants to go to the top/bottom of
|
||||
// the page try recursiveScroll instead
|
||||
recursiveScroll(window.scrollX, 1073741824 * (percentage == 0 ? -1 : 1), [window.document.body])
|
||||
// the page try scrolling.recursiveScroll instead
|
||||
scrolling.recursiveScroll(window.scrollX, 1073741824 * (percentage == 0 ? -1 : 1), [document.documentElement])
|
||||
}
|
||||
} else if (b === "x") {
|
||||
let left = elem.getClientRects()[0].left
|
||||
window.scrollTo(percentage * elem.scrollWidth / 100, window.scrollY)
|
||||
if (left == elem.getClientRects()[0].left && (percentage == 0 || percentage == 100)) {
|
||||
recursiveScroll(1073741824 * (percentage == 0 ? -1 : 1), window.scrollX, [window.document.body])
|
||||
scrolling.recursiveScroll(1073741824 * (percentage == 0 ? -1 : 1), window.scrollX, [document.documentElement])
|
||||
}
|
||||
} else {
|
||||
window.scrollTo(a, Number(b)) // a,b numbers
|
||||
}
|
||||
}
|
||||
|
||||
/** Tries to find a node which can be scrolled either x pixels to the right or
|
||||
* y pixels down among the Elements in {nodes} and children of these Elements.
|
||||
*
|
||||
* This function used to be recursive but isn't anymore due to various
|
||||
* attempts at optimizing the function in order to reduce GC pressure.
|
||||
*/
|
||||
//#content_helper
|
||||
function recursiveScroll(x: number, y: number, nodes: Element[]) {
|
||||
let index = 0
|
||||
do {
|
||||
let node = nodes[index++] as any
|
||||
// Save the node's position
|
||||
let top = node.scrollTop
|
||||
let left = node.scrollLeft
|
||||
node.scrollBy(x, y)
|
||||
// if the node moved, stop
|
||||
if (top != node.scrollTop || left != node.scrollLeft) return
|
||||
// Otherwise, add its children to the nodes that could be scrolled
|
||||
nodes = nodes.concat(Array.from(node.children))
|
||||
if (node.contentDocument) nodes.push(node.contentDocument.body)
|
||||
} while (index < nodes.length)
|
||||
}
|
||||
|
||||
//#content
|
||||
export function scrollline(n = 1) {
|
||||
let top = document.body.getClientRects()[0].top
|
||||
window.scrollByLines(n)
|
||||
if (top == document.body.getClientRects()[0].top) {
|
||||
const cssHeight = window.getComputedStyle(document.body).getPropertyValue("line-height")
|
||||
// Remove the "px" at the end
|
||||
const lineHeight = parseInt(cssHeight.substr(0, cssHeight.length - 2))
|
||||
// lineHeight probably can't be NaN but let's make sure
|
||||
if (lineHeight) recursiveScroll(0, lineHeight * n, [window.document.body])
|
||||
}
|
||||
const cssHeight = window.getComputedStyle(document.body).getPropertyValue("line-height")
|
||||
// Remove the "px" at the end
|
||||
const lineHeight = parseInt(cssHeight.substr(0, cssHeight.length - 2))
|
||||
// lineHeight probably can't be NaN but let's make sure
|
||||
if (lineHeight) scrolling.recursiveScroll(0, lineHeight * n, [document.documentElement])
|
||||
}
|
||||
|
||||
//#content
|
||||
|
|
|
@ -56,9 +56,7 @@ export class Logger {
|
|||
return browser.runtime.sendMessage({
|
||||
type: "commandline_background",
|
||||
command: "recvExStr",
|
||||
args: [
|
||||
"fillcmdline # Error: " + message.join(" "),
|
||||
],
|
||||
args: ["fillcmdline # " + message.join(" ")],
|
||||
})
|
||||
else
|
||||
return browser.tabs.sendMessage(
|
||||
|
@ -69,7 +67,7 @@ export class Logger {
|
|||
{
|
||||
type: "commandline_frame",
|
||||
command: "fillcmdline",
|
||||
args: ["# Error: " + message.join(" ")],
|
||||
args: ["# " + message.join(" ")],
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -361,12 +361,15 @@ export async function loadPrefs(filename): Promise<{ [key: string]: string }> {
|
|||
return parsePrefs(result.content)
|
||||
}
|
||||
|
||||
let cached_prefs = null
|
||||
|
||||
/** Returns a promise for an object that should contain every about:config
|
||||
* setting. If performance is slow, it might be a good idea to cache the
|
||||
* results of this function: the preference files do not change while firefox
|
||||
* is running.
|
||||
* setting.
|
||||
*
|
||||
* Performance is slow so we need to cache the results.
|
||||
*/
|
||||
export async function getPrefs(): Promise<{ [key: string]: string }> {
|
||||
if (cached_prefs != null) return cached_prefs
|
||||
const profile = (await getProfileDir()) + "/"
|
||||
const prefFiles = [
|
||||
// Debian has these
|
||||
|
@ -396,7 +399,10 @@ export async function getPrefs(): Promise<{ [key: string]: string }> {
|
|||
for (let file of prefFiles) {
|
||||
promises.push(loadPrefs(file))
|
||||
}
|
||||
return promises.reduce(async (a, b) => Object.assign(await a, await b))
|
||||
cached_prefs = promises.reduce(async (a, b) =>
|
||||
Object.assign(await a, await b),
|
||||
)
|
||||
return cached_prefs
|
||||
}
|
||||
|
||||
/** Returns the value for the corresponding about:config setting */
|
||||
|
@ -404,8 +410,42 @@ export async function getPref(name: string): Promise<string> {
|
|||
return (await getPrefs())[name]
|
||||
}
|
||||
|
||||
/** Fetches a config option from the config. If the option is undefined, fetch
|
||||
* a preference from preferences. It would make more sense for this function to
|
||||
* be in config.ts but this would require importing this file in config.ts and
|
||||
* Webpack doesn't like circular dependencies.
|
||||
*/
|
||||
export async function getConfElsePref(
|
||||
confName: string,
|
||||
prefName: string,
|
||||
): Promise<any> {
|
||||
let option = await config.getAsync(confName)
|
||||
if (option === undefined) {
|
||||
try {
|
||||
option = await getPref(prefName)
|
||||
} catch (e) {}
|
||||
}
|
||||
return option
|
||||
}
|
||||
|
||||
/** Fetches a config option from the config. If the option is undefined, fetch
|
||||
* prefName from the preferences. If prefName is undefined too, return a
|
||||
* default.
|
||||
*/
|
||||
export async function getConfElsePrefElseDefault(
|
||||
confName: string,
|
||||
prefName: string,
|
||||
def: any,
|
||||
): Promise<any> {
|
||||
let option = await getConfElsePref(confName, prefName)
|
||||
if (option === undefined) return def
|
||||
return option
|
||||
}
|
||||
|
||||
/** Writes a preference to user.js */
|
||||
export async function writePref(name: string, value: any) {
|
||||
if (cached_prefs) cached_prefs[name] = value
|
||||
|
||||
if (typeof value == "string") value = `"${value}"`
|
||||
const file = (await getProfileDir()) + "/user.js"
|
||||
// No need to check the return code because read returns "" when failing to
|
||||
|
|
162
src/scrolling.ts
Normal file
162
src/scrolling.ts
Normal file
|
@ -0,0 +1,162 @@
|
|||
import * as Native from "./native_background"
|
||||
|
||||
type scrollingDirection = "scrollLeft" | "scrollTop"
|
||||
|
||||
// Stores elements that are currently being horizontally scrolled
|
||||
let horizontallyScrolling = new Map()
|
||||
// Stores elements that are currently being vertically scrolled
|
||||
let verticallyScrolling = new Map()
|
||||
|
||||
async function getSmooth() {
|
||||
return await Native.getConfElsePrefElseDefault(
|
||||
"smoothscroll",
|
||||
"general.smoothScroll",
|
||||
"false",
|
||||
)
|
||||
}
|
||||
async function getDuration() {
|
||||
return await Native.getConfElsePrefElseDefault(
|
||||
"scrollduration",
|
||||
"general.smoothScroll.lines.durationMinMs",
|
||||
100,
|
||||
)
|
||||
}
|
||||
|
||||
class ScrollingData {
|
||||
// time at which the scrolling animation started
|
||||
startTime: number
|
||||
// Starting position of the element. This shouldn't ever change.
|
||||
startPos: number
|
||||
// Where the element should end up. This can change if .scroll() is called
|
||||
// while a scrolling animation is already running
|
||||
endPos: number
|
||||
// Whether the element is being scrolled
|
||||
scrolling = false
|
||||
// Duration of the scrolling animation
|
||||
duration = 0
|
||||
|
||||
/** elem: The element that should be scrolled
|
||||
* pos: "scrollLeft" if the element should be scrolled on the horizontal axis, "scrollTop" otherwise
|
||||
*/
|
||||
constructor(
|
||||
private elem: HTMLElement,
|
||||
private pos: scrollingDirection = "scrollTop",
|
||||
) {}
|
||||
|
||||
/** Computes where the element should be.
|
||||
* This changes depending on how long ago the first scrolling attempt was
|
||||
* made.
|
||||
* It might be useful to make this function more configurable by making it
|
||||
* accept an argument instead of using performance.now()
|
||||
*/
|
||||
getStep() {
|
||||
if (this.startTime === undefined) {
|
||||
this.startTime = performance.now()
|
||||
}
|
||||
let elapsed = performance.now() - this.startTime
|
||||
|
||||
// If the animation should be done, return the position the element should have
|
||||
if (elapsed > this.duration || this.elem[this.pos] == this.endPos)
|
||||
return this.endPos
|
||||
|
||||
let result = (this.endPos - this.startPos) * elapsed / this.duration
|
||||
if (result >= 1 || result <= -1) return this.startPos + result
|
||||
return this.elem[this.pos] + (this.startPos < this.endPos ? 1 : -1)
|
||||
}
|
||||
|
||||
/** Updates the position of this.elem */
|
||||
scrollStep() {
|
||||
let val = this.elem[this.pos]
|
||||
this.elem[this.pos] = this.getStep()
|
||||
return val != this.elem[this.pos]
|
||||
}
|
||||
|
||||
/** Calls this.scrollStep() until the element has been completely scrolled
|
||||
* or the scrolling animation is complete */
|
||||
scheduleStep() {
|
||||
// If scrollStep() scrolled the element, reschedule a step
|
||||
// Otherwise, register that the element stopped scrolling
|
||||
window.requestAnimationFrame(
|
||||
() =>
|
||||
this.scrollStep()
|
||||
? this.scheduleStep()
|
||||
: (this.scrolling = false),
|
||||
)
|
||||
}
|
||||
|
||||
scroll(distance: number, duration: number) {
|
||||
this.startTime = performance.now()
|
||||
this.startPos = this.elem[this.pos]
|
||||
this.endPos = this.elem[this.pos] + distance
|
||||
this.duration = duration
|
||||
// If we're already scrolling we don't need to try to scroll
|
||||
if (this.scrolling) return true
|
||||
this.scrolling = this.scrollStep()
|
||||
if (this.scrolling)
|
||||
// If the element can be scrolled, scroll until animation completion
|
||||
this.scheduleStep()
|
||||
return this.scrolling
|
||||
}
|
||||
}
|
||||
|
||||
/** Tries to scroll e by x and y pixel, make the smooth scrolling animation
|
||||
* last duration milliseconds
|
||||
*/
|
||||
export async function scroll(
|
||||
x: number,
|
||||
y: number,
|
||||
e: HTMLElement,
|
||||
duration: number = undefined,
|
||||
) {
|
||||
let smooth = await getSmooth()
|
||||
if (smooth == "false") duration = 0
|
||||
else if (duration === undefined) duration = await getDuration()
|
||||
|
||||
let result = false
|
||||
if (x != 0) {
|
||||
// Don't create a new ScrollingData object if the element is already
|
||||
// being scrolled
|
||||
let scrollData = horizontallyScrolling.get(e)
|
||||
if (!scrollData || !scrollData.scrolling)
|
||||
scrollData = new ScrollingData(e, "scrollLeft")
|
||||
horizontallyScrolling.set(e, scrollData)
|
||||
result = result || scrollData.scroll(x, duration)
|
||||
}
|
||||
if (y != 0) {
|
||||
let scrollData = verticallyScrolling.get(e)
|
||||
if (!scrollData || !scrollData.scrolling)
|
||||
scrollData = new ScrollingData(e, "scrollTop")
|
||||
verticallyScrolling.set(e, scrollData)
|
||||
result = result || scrollData.scroll(y, duration)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/** Tries to find a node which can be scrolled either x pixels to the right or
|
||||
* y pixels down among the Elements in {nodes} and children of these Elements.
|
||||
*
|
||||
* This function used to be recursive but isn't anymore due to various
|
||||
* attempts at optimizing the function in order to reduce GC pressure.
|
||||
*/
|
||||
export async function recursiveScroll(
|
||||
x: number,
|
||||
y: number,
|
||||
nodes: Element[],
|
||||
duration: number = undefined,
|
||||
) {
|
||||
// Determine whether to smoothscroll or not
|
||||
let smooth = await getSmooth()
|
||||
if (smooth == "false") duration = 0
|
||||
else if (duration === undefined) duration = await getDuration()
|
||||
|
||||
let index = 0
|
||||
let now = performance.now()
|
||||
do {
|
||||
let node = nodes[index++] as any
|
||||
// Save the node's position
|
||||
if (await scroll(x, y, node, duration)) return
|
||||
// Otherwise, add its children to the nodes that could be scrolled
|
||||
nodes = nodes.concat(Array.from(node.children))
|
||||
if (node.contentDocument) nodes.push(node.contentDocument.body)
|
||||
} while (index < nodes.length)
|
||||
}
|
6
src/tridactyl.d.ts
vendored
6
src/tridactyl.d.ts
vendored
|
@ -16,6 +16,12 @@ interface Window {
|
|||
eval(str: string): any
|
||||
}
|
||||
|
||||
// Again, firefox-specific
|
||||
interface UIEvent {
|
||||
pageX: number
|
||||
pageY: number
|
||||
}
|
||||
|
||||
interface HTMLElement {
|
||||
// Let's be future proof:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus
|
||||
|
|
Loading…
Add table
Reference in a new issue