mirror of
https://github.com/vale981/tridactyl
synced 2025-03-05 09:31:41 -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
|
web-ext-artifacts
|
||||||
yarn.lock
|
yarn.lock
|
||||||
.vscode/
|
.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 (
|
Param (
|
||||||
[switch]$Uninstall = $false,
|
[switch]$Uninstall = $false,
|
||||||
|
[switch]$NoPython= $false,
|
||||||
[string]$DebugDirBase = "",
|
[string]$DebugDirBase = "",
|
||||||
[string]$InstallDirBase = ""
|
[string]$InstallDirBase = ""
|
||||||
)
|
)
|
||||||
|
@ -8,26 +9,44 @@ Param (
|
||||||
# Global constants
|
# Global constants
|
||||||
#
|
#
|
||||||
$global:InstallDirName = ".tridactyl"
|
$global:InstallDirName = ".tridactyl"
|
||||||
$global:MessengerBinName = "native_main.py"
|
$global:MessengerBinPyName = "native_main.py"
|
||||||
|
$global:MessengerBinExeName = "native_main.exe"
|
||||||
$global:MessengerBinWrapperFilename = "native_main.bat"
|
$global:MessengerBinWrapperFilename = "native_main.bat"
|
||||||
$global:MessengerManifestFilename = "tridactyl.json"
|
$global:MessengerManifestFilename = "tridactyl.json"
|
||||||
$global:PythonVersionStr = "Python 3"
|
$global:PythonVersionStr = "Python 3"
|
||||||
|
$global:WinPython3Command = "py -3 -u"
|
||||||
$global:MessengerManifestReplaceStr = "REPLACE_ME_WITH_SED"
|
$global:MessengerManifestReplaceStr = "REPLACE_ME_WITH_SED"
|
||||||
|
|
||||||
$global:MessengerFilesHttpUriBase = [string]::Format("{0}{1}",
|
# $git_repo_owner should be "cmcaine" in final release
|
||||||
"https://raw.githubusercontent.com",
|
$git_repo_owner = "cmcaine"
|
||||||
"/cmcaine/tridactyl/master/native")
|
# $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 = `
|
$global:MessengerManifestRegistryPath = `
|
||||||
"HKCU:\Software\Mozilla\NativeMessagingHosts\tridactyl"
|
"HKCU:\Software\Mozilla\NativeMessagingHosts\tridactyl"
|
||||||
|
|
||||||
$global:Uninstall = $Uninstall
|
$global:Uninstall = $Uninstall
|
||||||
|
$global:NoPython= $NoPython
|
||||||
$global:InstallDirBase = $InstallDirBase.Trim()
|
$global:InstallDirBase = $InstallDirBase.Trim()
|
||||||
$global:DebugDirBase = $DebugDirBase.Trim()
|
$global:DebugDirBase = $DebugDirBase.Trim()
|
||||||
|
|
||||||
function Get-PythonVersionStatus() {
|
function Get-PythonVersionStatus() {
|
||||||
try {
|
try {
|
||||||
$pythonVersion = py -3 -u --version
|
$pythonVersion = Invoke-Expression `
|
||||||
|
"$global:WinPython3Command --version"
|
||||||
} catch {
|
} catch {
|
||||||
$pythonVersion = ""
|
$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() {
|
function Get-MessengerBinPath() {
|
||||||
$messengerInstallDir = Get-MessengerInstallDir
|
$messengerInstallDir = Get-MessengerInstallDir
|
||||||
|
$messengerBinName = Get-MessengerBinName
|
||||||
|
|
||||||
$native_messenger_binary_path = [string]::Format("{0}\{1}",
|
$native_messenger_binary_path = [string]::Format("{0}\{1}",
|
||||||
$messengerInstallDir,
|
$messengerInstallDir,
|
||||||
$global:MessengerBinName)
|
$messengerBinName)
|
||||||
|
|
||||||
Return $native_messenger_binary_path.Trim()
|
Return $native_messenger_binary_path.Trim()
|
||||||
}
|
}
|
||||||
|
@ -165,10 +194,18 @@ function Get-MessengerBinUri() {
|
||||||
-Date ((Get-Date).ToUniversalTime()) `
|
-Date ((Get-Date).ToUniversalTime()) `
|
||||||
-UFormat %s)
|
-UFormat %s)
|
||||||
|
|
||||||
$messengerBinUri = [string]::Format("{0}/{1}?{2}",
|
$messengerBinName = Get-MessengerBinName
|
||||||
$global:MessengerFilesHttpUriBase,
|
if ($global:NoPython -eq $true) { # system doesn't have python3
|
||||||
$global:MessengerBinName,
|
$messengerBinUri = [string]::Format("{0}/{1}",
|
||||||
$downloadStartTime)
|
$global:MessengerExeHttpUriBase,
|
||||||
|
$messengerBinName
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
$messengerBinUri = [string]::Format("{0}/{1}?{2}",
|
||||||
|
$global:MessengerFilesHttpUriBase,
|
||||||
|
$messengerBinName,
|
||||||
|
$downloadStartTime)
|
||||||
|
}
|
||||||
|
|
||||||
Return $messengerBinUri.Trim()
|
Return $messengerBinUri.Trim()
|
||||||
}
|
}
|
||||||
|
@ -178,15 +215,18 @@ function Set-MessengerBin() {
|
||||||
$messengerBinUri = Get-MessengerBinUri
|
$messengerBinUri = Get-MessengerBinUri
|
||||||
|
|
||||||
if ($global:DebugDirBase.Length -gt 0) {
|
if ($global:DebugDirBase.Length -gt 0) {
|
||||||
|
$messengerBinName = Get-MessengerBinName
|
||||||
|
|
||||||
$srcPath = [string]::Format("{0}\{1}",
|
$srcPath = [string]::Format("{0}\{1}",
|
||||||
$global:DebugDirBase,
|
$global:DebugDirBase,
|
||||||
$global:MessengerBinName)
|
$messengerBinName)
|
||||||
|
|
||||||
Write-Host "[+] Copying $srcPath ..."
|
Write-Host "[+] Copying $srcPath ..."
|
||||||
|
|
||||||
Copy-Item `
|
Copy-Item `
|
||||||
-Path $srcPath `
|
-Path $srcPath `
|
||||||
-Destination $messengerBinPath
|
-Destination $messengerBinPath `
|
||||||
|
-Force `
|
||||||
} else {
|
} else {
|
||||||
Write-Host "[+] Downloading $messengerBinUri ..."
|
Write-Host "[+] Downloading $messengerBinUri ..."
|
||||||
|
|
||||||
|
@ -233,10 +273,18 @@ function Get-MessengerBinWrapperPath() {
|
||||||
function Set-MessengerBinWrapper() {
|
function Set-MessengerBinWrapper() {
|
||||||
$messengerBinPath = Get-MessengerBinPath
|
$messengerBinPath = Get-MessengerBinPath
|
||||||
$messengerBinWrapperPath = Get-MessengerBinWrapperPath
|
$messengerBinWrapperPath = Get-MessengerBinWrapperPath
|
||||||
|
|
||||||
|
if ($global:NoPython -eq $false) { # system has python3
|
||||||
$messengerWrapperContent = @"
|
$messengerWrapperContent = @"
|
||||||
@echo off
|
@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 ..."
|
Write-Host "[+] Preparing $messengerBinWrapperPath ..."
|
||||||
|
|
||||||
|
@ -298,7 +346,8 @@ function Set-MessengerManifest() {
|
||||||
|
|
||||||
Copy-Item `
|
Copy-Item `
|
||||||
-Path $srcPath `
|
-Path $srcPath `
|
||||||
-Destination $messengerManifestPath
|
-Destination $messengerManifestPath `
|
||||||
|
-Force `
|
||||||
} else {
|
} else {
|
||||||
Write-Host "[+] Downloading $messengerManifestUri ..."
|
Write-Host "[+] Downloading $messengerManifestUri ..."
|
||||||
|
|
||||||
|
@ -437,23 +486,26 @@ function Set-MessengerManifestRegistry() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function Set-MessengerInstall() {
|
function Set-MessengerInstall() {
|
||||||
# Check for Python 3
|
# Check if system has Python 3, unless user set # the
|
||||||
Write-Host "[+] Looking for Python 3 ..."
|
# `-NoPython` flag
|
||||||
$pythonVersionStatus = Get-PythonVersionStatus
|
if ($global:NoPython -eq $false) {
|
||||||
if (! $pythonVersionStatus) {
|
Write-Host "[+] Looking for Python 3 ..."
|
||||||
Write-Host " - Python 3 not found, quitting ..."
|
$pythonVersionStatus = Get-PythonVersionStatus
|
||||||
exit -1
|
if (! $pythonVersionStatus) {
|
||||||
} else {
|
Write-Host " - Python 3 not found, will use EXE ..."
|
||||||
$pythonPath = Get-Command "py" `
|
$global:NoPython = $true
|
||||||
|
} else {
|
||||||
|
$pythonPath = Get-Command "py" `
|
||||||
| Select-Object -ExpandProperty "Source"
|
| Select-Object -ExpandProperty "Source"
|
||||||
|
|
||||||
Write-Host " - Python 3 found at: $pythonPath"
|
Write-Host " - Python 3 found at: $pythonPath"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Prepare `.tridactyl` directory
|
# Prepare `.tridactyl` directory
|
||||||
$result = Set-InstallDir
|
$result = Set-InstallDir
|
||||||
|
|
||||||
# Prepare `native_main.py`
|
# Prepare `native_main.{py,exe}`
|
||||||
if ($result -eq $true) {
|
if ($result -eq $true) {
|
||||||
$result = Set-MessengerBin
|
$result = Set-MessengerBin
|
||||||
}
|
}
|
||||||
|
@ -503,4 +555,3 @@ if ($global:Uninstall) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Set-MessengerInstall
|
Set-MessengerInstall
|
||||||
|
|
||||||
|
|
39
package-lock.json
generated
39
package-lock.json
generated
|
@ -2245,7 +2245,8 @@
|
||||||
"discontinuous-range": {
|
"discontinuous-range": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
|
||||||
"integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo="
|
"integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"dispensary": {
|
"dispensary": {
|
||||||
"version": "0.10.10",
|
"version": "0.10.10",
|
||||||
|
@ -5781,9 +5782,9 @@
|
||||||
"integrity": "sha1-GA8fnr74sOY45BZq1S24eb6y/8U="
|
"integrity": "sha1-GA8fnr74sOY45BZq1S24eb6y/8U="
|
||||||
},
|
},
|
||||||
"marked": {
|
"marked": {
|
||||||
"version": "0.3.6",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/marked/-/marked-0.4.0.tgz",
|
||||||
"integrity": "sha1-ssbGGPzOzk74bE/Gy4p8v1rtqNc=",
|
"integrity": "sha512-tMsdNBgOsrUophCAFQl0XPe6Zqk/uy9gnue+jIIKhykO51hxyu6uNx7zBPy0+y/WKYVZZMspV9YeXLNdKk+iYw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"md5.js": {
|
"md5.js": {
|
||||||
|
@ -6096,13 +6097,15 @@
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"nearley": {
|
"nearley": {
|
||||||
"version": "2.11.0",
|
"version": "2.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/nearley/-/nearley-2.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/nearley/-/nearley-2.13.0.tgz",
|
||||||
"integrity": "sha512-clqqhEuP0ZCJQ85Xv2I/4o2Gs/fvSR6fCg5ZHVE2c8evWyNk2G++ih4JOO3lMb/k/09x6ihQ2nzKUlB/APCWjg==",
|
"integrity": "sha512-ioYYogSaZhFlCpRizQgY3UT3G1qFXmHGY/5ozoFE3dMfiCRAeJfh+IPE3/eh9gCZvqLhPCWb4bLt7Bqzo+1mLQ==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"nomnom": "~1.6.2",
|
"nomnom": "~1.6.2",
|
||||||
"railroad-diagrams": "^1.0.0",
|
"railroad-diagrams": "^1.0.0",
|
||||||
"randexp": "^0.4.2"
|
"randexp": "0.4.6",
|
||||||
|
"semver": "^5.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node-dir": {
|
"node-dir": {
|
||||||
|
@ -6193,6 +6196,7 @@
|
||||||
"version": "1.6.2",
|
"version": "1.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz",
|
||||||
"integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=",
|
"integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"colors": "0.5.x",
|
"colors": "0.5.x",
|
||||||
"underscore": "~1.4.4"
|
"underscore": "~1.4.4"
|
||||||
|
@ -6201,7 +6205,8 @@
|
||||||
"colors": {
|
"colors": {
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz",
|
"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": {
|
"railroad-diagrams": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
|
||||||
"integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234="
|
"integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"randexp": {
|
"randexp": {
|
||||||
"version": "0.4.6",
|
"version": "0.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
|
"resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
|
||||||
"integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
|
"integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"discontinuous-range": "1.0.0",
|
"discontinuous-range": "1.0.0",
|
||||||
"ret": "~0.1.10"
|
"ret": "~0.1.10"
|
||||||
|
@ -7300,7 +7307,8 @@
|
||||||
"ret": {
|
"ret": {
|
||||||
"version": "0.1.15",
|
"version": "0.1.15",
|
||||||
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
|
"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": {
|
"right-align": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
|
@ -8786,6 +8794,12 @@
|
||||||
"typescript": "2.4.1"
|
"typescript": "2.4.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"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": {
|
"progress": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
|
||||||
|
@ -8855,7 +8869,8 @@
|
||||||
"underscore": {
|
"underscore": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
|
||||||
"integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ="
|
"integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"union-value": {
|
"union-value": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
"css": "^2.2.1",
|
"css": "^2.2.1",
|
||||||
"fuse.js": "^3.2.0",
|
"fuse.js": "^3.2.0",
|
||||||
"mark.js": "^8.11.1",
|
"mark.js": "^8.11.1",
|
||||||
"nearley": "^2.11.0",
|
|
||||||
"semver-compare": "^1.0.0"
|
"semver-compare": "^1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -20,6 +19,8 @@
|
||||||
"cleanslate": "^0.10.1",
|
"cleanslate": "^0.10.1",
|
||||||
"copy-webpack-plugin": "^4.2.0",
|
"copy-webpack-plugin": "^4.2.0",
|
||||||
"jest": "^21.2.1",
|
"jest": "^21.2.1",
|
||||||
|
"marked": "^0.4.0",
|
||||||
|
"nearley": "^2.13.0",
|
||||||
"prettier": "^1.11.1",
|
"prettier": "^1.11.1",
|
||||||
"shared-git-hooks": "^1.2.1",
|
"shared-git-hooks": "^1.2.1",
|
||||||
"source-map-loader": "^0.2.2",
|
"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
|
* `;k` — delete an element from the page
|
||||||
* `;;` — focus an element
|
* `;;` — 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
|
### 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.
|
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]
|
* 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-git]: https://git-scm.com/download/win
|
||||||
[win-nodejs]: https://nodejs.org/dist/v8.11.1/node-v8.11.1-x64.msi
|
[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
|
### Development loop
|
||||||
|
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
# Put the AMO flavour text in your clipboard for easy pasting.
|
# Put the AMO flavour text in your clipboard for easy pasting.
|
||||||
# AMO doesn't support all HTML in markdown so we strip it out.
|
# 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() {
|
isWindowsMinGW() {
|
||||||
local is_mingw="False"
|
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"
|
is_mingw="True"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ scripts/newtab.md.sh
|
||||||
scripts/make_tutorial.sh
|
scripts/make_tutorial.sh
|
||||||
scripts/make_docs.sh &
|
scripts/make_docs.sh &
|
||||||
|
|
||||||
nearleyc src/grammars/bracketexpr.ne \
|
$(npm bin)/nearleyc src/grammars/bracketexpr.ne \
|
||||||
> src/grammars/.bracketexpr.generated.ts
|
> src/grammars/.bracketexpr.generated.ts
|
||||||
|
|
||||||
if [ "$(isWindowsMinGW)" = "True" ]; then
|
if [ "$(isWindowsMinGW)" = "True" ]; then
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
dest=generated/static/docs
|
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/
|
cp -r $dest build/static/
|
||||||
|
|
|
@ -11,6 +11,6 @@ for page in $pages
|
||||||
do
|
do
|
||||||
fileroot=$(echo $page | cut -d'.' -f-1)
|
fileroot=$(echo $page | cut -d'.' -f-1)
|
||||||
sed "/REPLACETHIS/,$ d" tutor.template.html > "$dest$fileroot.html"
|
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"
|
sed "1,/REPLACETHIS/ d" tutor.template.html >> "$dest$fileroot.html"
|
||||||
done
|
done
|
||||||
|
|
|
@ -8,7 +8,7 @@ newtab="../../generated/static/newtab.html"
|
||||||
newtabtemp="../../generated/static/newtab.temp.html"
|
newtabtemp="../../generated/static/newtab.temp.html"
|
||||||
|
|
||||||
sed "/REPLACETHIS/,$ d" newtab.template.html > "$newtabtemp"
|
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"
|
sed "1,/REPLACETHIS/ d" newtab.template.html >> "$newtabtemp"
|
||||||
|
|
||||||
# Why think when you can pattern match?
|
# Why think when you can pattern match?
|
||||||
|
@ -19,7 +19,7 @@ echo """
|
||||||
<label for="spoilerbutton" onclick="">Changelog</label>
|
<label for="spoilerbutton" onclick="">Changelog</label>
|
||||||
<div class="spoiler">
|
<div class="spoiler">
|
||||||
""" >> "$newtab"
|
""" >> "$newtab"
|
||||||
marked ../../CHANGELOG.md >> "$newtab"
|
$(npm bin)/marked ../../CHANGELOG.md >> "$newtab"
|
||||||
echo """
|
echo """
|
||||||
</div>
|
</div>
|
||||||
""" >> "$newtab"
|
""" >> "$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 #",
|
"<c-6>": "buffer #",
|
||||||
H: "back",
|
H: "back",
|
||||||
L: "forward",
|
L: "forward",
|
||||||
"<c-o>": "back",
|
"<c-o>": "jumpprev",
|
||||||
"<c-i>": "forward",
|
"<c-i>": "jumpnext",
|
||||||
d: "tabclose",
|
d: "tabclose",
|
||||||
D: "composite tabprev; sleep 100; tabclose #",
|
D: "composite tabprev; sleep 100; tabclose #",
|
||||||
gx0: "tabclosealltoleft",
|
gx0: "tabclosealltoleft",
|
||||||
|
@ -233,6 +233,11 @@ const DEFAULTS = o({
|
||||||
// Maybe have a nice user-vicible message when the setting is changed?
|
// Maybe have a nice user-vicible message when the setting is changed?
|
||||||
allowautofocus: "true",
|
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",
|
tabopenpos: "next",
|
||||||
relatedopenpos: "related",
|
relatedopenpos: "related",
|
||||||
ttsvoice: "default", // chosen from the listvoices list, or "default"
|
ttsvoice: "default", // chosen from the listvoices list, or "default"
|
||||||
|
@ -252,6 +257,8 @@ const DEFAULTS = o({
|
||||||
theme: "default", // currently available: "default", "dark"
|
theme: "default", // currently available: "default", "dark"
|
||||||
modeindicator: "true",
|
modeindicator: "true",
|
||||||
|
|
||||||
|
jumpdelay: 3000, // Milliseconds before registering a scroll in the jumplist
|
||||||
|
|
||||||
// Default logging levels - 2 === WARNING
|
// Default logging levels - 2 === WARNING
|
||||||
logging: o({
|
logging: o({
|
||||||
messaging: 2,
|
messaging: 2,
|
||||||
|
@ -274,9 +281,9 @@ const DEFAULTS = o({
|
||||||
nativeinstallcmd:
|
nativeinstallcmd:
|
||||||
"curl -fsSl https://raw.githubusercontent.com/cmcaine/tridactyl/master/native/install.sh | bash",
|
"curl -fsSl https://raw.githubusercontent.com/cmcaine/tridactyl/master/native/install.sh | bash",
|
||||||
win_powershell_nativeinstallcmd:
|
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:
|
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",
|
profiledir: "auto",
|
||||||
|
|
||||||
// Container settings
|
// 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))
|
Messaging.addListener("excmd_content", Messaging.attributeCaller(SELF))
|
||||||
import * as DOM from "./dom"
|
import * as DOM from "./dom"
|
||||||
import { executeWithoutCommandLine } from "./commandline_content"
|
import { executeWithoutCommandLine } from "./commandline_content"
|
||||||
|
import * as scrolling from "./scrolling"
|
||||||
// }
|
// }
|
||||||
|
|
||||||
//#background_helper
|
//#background_helper
|
||||||
|
@ -514,6 +515,110 @@ export function loggingsetlevel(logModule: string, level: string) {
|
||||||
|
|
||||||
// {{{ PAGE CONTEXT
|
// {{{ 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 */
|
/** Blur (unfocus) the active element */
|
||||||
//#content
|
//#content
|
||||||
export function unfocus() {
|
export function unfocus() {
|
||||||
|
@ -522,10 +627,8 @@ export function unfocus() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//#content
|
//#content
|
||||||
export function scrollpx(a: number, b: number) {
|
export async function scrollpx(a: number, b: number) {
|
||||||
let top = document.body.getClientRects()[0].top
|
if (!await scrolling.scroll(a, b, document.documentElement)) scrolling.recursiveScroll(a, b, [document.documentElement])
|
||||||
window.scrollBy(a, b)
|
|
||||||
if (top == document.body.getClientRects()[0].top) recursiveScroll(a, b, [document.body])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** If two numbers are given, treat as x and y values to give to window.scrollTo
|
/** 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)
|
window.scrollTo(window.scrollX, percentage * elem.scrollHeight / 100)
|
||||||
if (top == elem.getClientRects()[0].top && (percentage == 0 || percentage == 100)) {
|
if (top == elem.getClientRects()[0].top && (percentage == 0 || percentage == 100)) {
|
||||||
// scrollTo failed, if the user wants to go to the top/bottom of
|
// scrollTo failed, if the user wants to go to the top/bottom of
|
||||||
// the page try recursiveScroll instead
|
// the page try scrolling.recursiveScroll instead
|
||||||
recursiveScroll(window.scrollX, 1073741824 * (percentage == 0 ? -1 : 1), [window.document.body])
|
scrolling.recursiveScroll(window.scrollX, 1073741824 * (percentage == 0 ? -1 : 1), [document.documentElement])
|
||||||
}
|
}
|
||||||
} else if (b === "x") {
|
} else if (b === "x") {
|
||||||
let left = elem.getClientRects()[0].left
|
let left = elem.getClientRects()[0].left
|
||||||
window.scrollTo(percentage * elem.scrollWidth / 100, window.scrollY)
|
window.scrollTo(percentage * elem.scrollWidth / 100, window.scrollY)
|
||||||
if (left == elem.getClientRects()[0].left && (percentage == 0 || percentage == 100)) {
|
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 {
|
} else {
|
||||||
window.scrollTo(a, Number(b)) // a,b numbers
|
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
|
//#content
|
||||||
export function scrollline(n = 1) {
|
export function scrollline(n = 1) {
|
||||||
let top = document.body.getClientRects()[0].top
|
const cssHeight = window.getComputedStyle(document.body).getPropertyValue("line-height")
|
||||||
window.scrollByLines(n)
|
// Remove the "px" at the end
|
||||||
if (top == document.body.getClientRects()[0].top) {
|
const lineHeight = parseInt(cssHeight.substr(0, cssHeight.length - 2))
|
||||||
const cssHeight = window.getComputedStyle(document.body).getPropertyValue("line-height")
|
// lineHeight probably can't be NaN but let's make sure
|
||||||
// Remove the "px" at the end
|
if (lineHeight) scrolling.recursiveScroll(0, lineHeight * n, [document.documentElement])
|
||||||
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])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//#content
|
//#content
|
||||||
|
|
|
@ -56,9 +56,7 @@ export class Logger {
|
||||||
return browser.runtime.sendMessage({
|
return browser.runtime.sendMessage({
|
||||||
type: "commandline_background",
|
type: "commandline_background",
|
||||||
command: "recvExStr",
|
command: "recvExStr",
|
||||||
args: [
|
args: ["fillcmdline # " + message.join(" ")],
|
||||||
"fillcmdline # Error: " + message.join(" "),
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
return browser.tabs.sendMessage(
|
return browser.tabs.sendMessage(
|
||||||
|
@ -69,7 +67,7 @@ export class Logger {
|
||||||
{
|
{
|
||||||
type: "commandline_frame",
|
type: "commandline_frame",
|
||||||
command: "fillcmdline",
|
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)
|
return parsePrefs(result.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let cached_prefs = null
|
||||||
|
|
||||||
/** Returns a promise for an object that should contain every about:config
|
/** 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
|
* setting.
|
||||||
* results of this function: the preference files do not change while firefox
|
*
|
||||||
* is running.
|
* Performance is slow so we need to cache the results.
|
||||||
*/
|
*/
|
||||||
export async function getPrefs(): Promise<{ [key: string]: string }> {
|
export async function getPrefs(): Promise<{ [key: string]: string }> {
|
||||||
|
if (cached_prefs != null) return cached_prefs
|
||||||
const profile = (await getProfileDir()) + "/"
|
const profile = (await getProfileDir()) + "/"
|
||||||
const prefFiles = [
|
const prefFiles = [
|
||||||
// Debian has these
|
// Debian has these
|
||||||
|
@ -396,7 +399,10 @@ export async function getPrefs(): Promise<{ [key: string]: string }> {
|
||||||
for (let file of prefFiles) {
|
for (let file of prefFiles) {
|
||||||
promises.push(loadPrefs(file))
|
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 */
|
/** 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]
|
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 */
|
/** Writes a preference to user.js */
|
||||||
export async function writePref(name: string, value: any) {
|
export async function writePref(name: string, value: any) {
|
||||||
|
if (cached_prefs) cached_prefs[name] = value
|
||||||
|
|
||||||
if (typeof value == "string") value = `"${value}"`
|
if (typeof value == "string") value = `"${value}"`
|
||||||
const file = (await getProfileDir()) + "/user.js"
|
const file = (await getProfileDir()) + "/user.js"
|
||||||
// No need to check the return code because read returns "" when failing to
|
// 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
|
eval(str: string): any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Again, firefox-specific
|
||||||
|
interface UIEvent {
|
||||||
|
pageX: number
|
||||||
|
pageY: number
|
||||||
|
}
|
||||||
|
|
||||||
interface HTMLElement {
|
interface HTMLElement {
|
||||||
// Let's be future proof:
|
// Let's be future proof:
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus
|
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus
|
||||||
|
|
Loading…
Add table
Reference in a new issue