2017-10-12 04:02:01 +01:00
// '//#' is a start point for a simple text-replacement-type macro. See excmds_macros.py
2017-11-19 06:05:15 +00:00
/ * * # T r i d a c t y l h e l p p a g e
Use ` :help <excmd> ` or scroll down to show [ [ help ] ] for a particular excmd .
2017-11-22 12:13:25 +00:00
The default keybinds can be found [ here ] ( / s t a t i c / d o c s / m o d u l e s / _ p a r s e r s _ n o r m a l m o d e _ . h t m l # d e f a u l t n m a p s ) .
2017-11-19 06:05:15 +00:00
Tridactyl is in a pretty early stage of development . Please report any issues and make requests for missing features on the GitHub project page [ [ 1 ] ] .
Highlighted features :
- Press ` b ` to bring up a list of open tabs in the current window ; you can type the tab ID or part of the title or URL to choose a tab ( the buffer list doesn 't show which one you' ve selected yet , but it does work )
- Press ` I ` to enter ignore mode . ` Shift ` + ` Escape ` to return to normal mode .
- Press ` f ` to start "hint mode" , ` F ` to open in background
- Press ` o ` to ` :open ` a different page
- Press ` s ` if you want to search for something that looks like a domain name or URL
- [ [ bind ] ] new commands with e . g . ` :bind J tabnext `
- Type ` :help ` to see a list of available excmds
- Use ` yy ` to copy the current page URL to your clipboard
- ` ]] ` and ` [[ ` to navigate through the pages of comics , paginated articles , etc
- Pressing ` ZZ ` will close all tabs and windows , but it will only "save" them if your about :preferences are set to "show your tabs and windows from last time"
There are some caveats common to all webextension vimperator - alikes :
- Do not try to navigate to any about : \ * pages using ` :open ` as it will fail silently
- Firefox will not load Tridactyl on addons . mozilla . org , about : \ * , some file : \ * URIs , view - source : \ * , or data : \ * . On these pages Ctrl - L ( or F6 ) , Ctrl - Tab and Ctrl - W are your escape hatches
- Tridactyl does not currently support changing / hiding the Firefox GUI , but you can do it yourself by changing your userChrome . There is an example file available on our repository [ [ 2 ] ]
If you want a more fully - featured vimperator - alike , your best option is Firefox ESR [ [ 3 ] ] and Vimperator : )
[ 1 ] : https : //github.com/cmcaine/tridactyl/issues
[ 2 ] : https : //github.com/cmcaine/tridactyl/blob/master/src/static/userChrome-minimal.css
[ 3 ] : https : //www.mozilla.org/en-US/firefox/organizations/
* /
/** ignore this line */
2017-10-12 04:02:01 +01:00
// {{{ setup
2017-10-23 09:42:50 +01:00
import * as Messaging from "./messaging"
2017-11-15 00:41:53 +00:00
import { l } from './lib/webext'
2017-10-23 09:42:50 +01:00
2017-10-12 04:02:01 +01:00
//#content_omit_line
import "./number.clamp"
//#content_helper
import * as SELF from "./excmds_content"
2017-10-23 09:42:50 +01:00
//#content_helper
Messaging . addListener ( 'excmd_content' , Messaging . attributeCaller ( SELF ) )
2017-10-28 05:11:10 +01:00
/** Message excmds_content.ts in the active tab of the currentWindow */
//#background_helper
import { messageActiveTab } from './messaging'
2017-10-12 04:02:01 +01:00
//#background_helper
import "./number.mod"
//#background_helper
import state from "./state"
2017-10-23 09:42:50 +01:00
//#background_helper
2017-11-09 00:41:07 +00:00
import { ModeName } from './state'
//#background_helper
2017-10-23 09:42:50 +01:00
import * as keydown from "./keydown_background"
2017-11-09 00:41:07 +00:00
//#background_helper
2017-11-21 19:10:42 +00:00
import { activeTab , activeTabId , firefoxVersionAtLeast } from './lib/webext'
2017-11-18 16:52:23 +00:00
//#content_helper
2017-11-21 10:52:30 +00:00
import { incrementUrl , getUrlRoot , getUrlParent } from "./url_util"
2017-11-22 18:05:54 +00:00
//#background_helper
import * as CommandLineBackground from './commandline_background'
2017-11-23 23:57:04 +00:00
//#content_helper
import * as DOM from './dom'
2017-11-29 16:40:16 +00:00
import * as config from './config'
2017-10-12 04:02:01 +01:00
2017-11-03 19:10:12 +00:00
/** @hidden */
2017-10-12 04:02:01 +01:00
//#background_helper
2017-10-23 09:42:50 +01:00
export const cmd_params = new Map < string , Map < string , string > > ( )
2017-10-12 04:02:01 +01:00
2017-11-14 23:31:08 +00:00
const SEARCH_URLS = new Map < string , string > ( [
[ "google" , "https://www.google.com/search?q=" ] ,
[ "googleuk" , "https://www.google.co.uk/search?q=" ] ,
[ "bing" , "https://www.bing.com/search?q=" ] ,
[ "duckduckgo" , "https://duckduckgo.com/?q=" ] ,
[ "yahoo" , "https://search.yahoo.com/search?p=" ] ,
[ "twitter" , "https://twitter.com/search?q=" ] ,
2017-11-20 01:08:26 +00:00
[ "wikipedia" , "https://en.wikipedia.org/wiki/Special:Search/" ] ,
2017-11-14 23:31:08 +00:00
[ "youtube" , "https://www.youtube.com/results?search_query=" ] ,
[ "amazon" , "https://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=" ] ,
[ "amazonuk" , "https://www.amazon.co.uk/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=" ] ,
2017-11-19 21:00:58 +01:00
[ "startpage" , "https://www.startpage.com/do/search?query=" ] ,
2017-11-26 16:36:07 +01:00
[ "github" , "https://github.com/search?utf8=✓&q=" ] ,
[ "searx" , "https://searx.me/?category_general=on&q=" ] ,
[ "cnrtl" , "http://www.cnrtl.fr/lexicographie/" ] ,
[ "osm" , "https://www.openstreetmap.org/search?query=" ] ,
[ "mdn" , "https://developer.mozilla.org/en-US/search?q=" ] ,
[ "gentoo_wiki" , "https://wiki.gentoo.org/index.php?title=Special%3ASearch&profile=default&fulltext=Search&search=" ] ,
2017-11-14 23:31:08 +00:00
] )
2017-10-22 16:33:43 +01:00
2017-11-22 22:21:46 +00:00
// map a page-relation (next or previous) to a fallback pattern to match link texts against
const REL_PATTERN = {
next : /^(?:next|newer)\b|»|>>/i ,
prev : /^(?:prev(?:ious)?|older)\b|«|<</i ,
}
2017-11-03 19:10:12 +00:00
/** @hidden */
2017-10-12 04:02:01 +01:00
function hasScheme ( uri : string ) {
2017-11-19 06:05:15 +00:00
return uri . match ( /^([\w-]+):/ )
2017-10-12 04:02:01 +01:00
}
2017-11-20 01:11:38 +00:00
/** @hidden */
function searchURL ( provider : string , query : string ) {
2017-11-29 19:51:18 +00:00
if ( provider == "search" ) provider = config . get ( "searchengine" )
2017-11-20 01:11:38 +00:00
if ( SEARCH_URLS . has ( provider ) ) {
2017-11-24 23:30:24 +01:00
const url = new URL ( SEARCH_URLS . get ( provider ) + encodeURIComponent ( query ) )
2017-11-20 01:11:38 +00:00
// URL constructor doesn't convert +s because they're valid literals in
// the standard it adheres to. But they are special characters in
// x-www-form-urlencoded and e.g. google excepts query parameters in
// that format.
url . search = url . search . replace ( /\+/g , '%2B' )
return url
} else {
throw new TypeError ( ` Unknown provider: ' ${ provider } ' ` )
}
}
2017-10-12 04:02:01 +01:00
/** If maybeURI doesn't have a schema, affix http:// */
2017-11-03 19:10:12 +00:00
/** @hidden */
2017-11-20 01:11:38 +00:00
function forceURI ( maybeURI : string ) : string {
try {
return new URL ( maybeURI ) . href
} catch ( e ) {
if ( e . name !== 'TypeError' ) throw e
2017-11-09 07:38:24 +00:00
}
2017-11-15 00:55:51 +02:00
2017-11-20 01:11:38 +00:00
// Else if search keyword:
try {
const args = maybeURI . split ( ' ' )
return searchURL ( args [ 0 ] , args . slice ( 1 ) . join ( ' ' ) ) . href
} catch ( e ) {
console . log ( e )
if ( e . name !== 'TypeError' ) throw e
}
// Else if it's a domain or something
try {
const url = new URL ( 'http://' + maybeURI )
// Ignore unlikely domains
if ( url . hostname . includes ( '.' ) || url . port || url . password ) {
return url . href
}
} catch ( e ) {
if ( e . name !== 'TypeError' ) throw e
2017-10-12 04:02:01 +01:00
}
2017-11-20 01:11:38 +00:00
2017-11-29 19:51:18 +00:00
// Else search $searchengine
2017-11-29 16:40:16 +00:00
return searchURL ( 'search' , maybeURI ) . href
2017-10-12 04:02:01 +01:00
}
2017-11-03 19:10:12 +00:00
/** @hidden */
2017-10-12 04:02:01 +01:00
//#background_helper
function tabSetActive ( id : number ) {
browser . tabs . update ( id , { active : true } )
}
// }}}
2017-11-22 18:05:54 +00:00
2017-10-12 04:02:01 +01:00
// {{{ PAGE CONTEXT
2017-11-09 21:01:57 +00:00
/** Blur (unfocus) the active element */
//#content
export function unfocus() {
( document . activeElement as HTMLInputElement ) . blur ( )
}
2017-10-12 04:02:01 +01:00
//#content
export function scrollpx ( a : number , b : number ) {
window . scrollBy ( a , b )
}
/ * * I f o n e a r g u m e n t i s g i v e n , s c r o l l t o t h a t p e r c e n t a g e d o w n t h e p a g e .
If two arguments are given , treat as x and y values to give to window . scrollTo
* /
//#content
export function scrollto ( a : number , b? : number ) {
a = Number ( a )
// if b is undefined, Number(b) is NaN.
b = Number ( b )
window . scrollTo (
b ? a : window.scrollX ,
b
? b
: a . clamp ( 0 , 100 ) *
( window . document . scrollingElement . scrollHeight / 100 )
)
}
//#content
export function scrollline ( n = 1 ) {
window . scrollByLines ( n )
}
//#content
export function scrollpage ( n = 1 ) {
2017-11-07 12:05:14 +00:00
window . scrollBy ( 0 , window . innerHeight * n )
2017-10-12 04:02:01 +01:00
}
2017-11-03 19:10:12 +00:00
/** @hidden */
2017-10-12 04:02:01 +01:00
//#content_helper
function history ( n : number ) {
window . history . go ( n )
}
2017-11-19 06:05:15 +00:00
/** Navigate forward one page in history. */
2017-10-12 04:02:01 +01:00
//#content
export function forward ( n = 1 ) {
history ( n )
}
2017-11-19 06:05:15 +00:00
/** Navigate back one page in history. */
2017-10-12 04:02:01 +01:00
//#content
export function back ( n = 1 ) {
history ( n * - 1 )
}
/** Reload the next n tabs, starting with activeTab, possibly bypassingCache */
//#background
export async function reload ( n = 1 , hard = false ) {
2017-11-09 00:41:07 +00:00
let tabstoreload = await getnexttabs ( await activeTabId ( ) , n )
2017-10-12 04:02:01 +01:00
let reloadProperties = { bypassCache : hard }
tabstoreload . map ( n = > browser . tabs . reload ( n , reloadProperties ) )
}
2017-11-22 16:59:58 +00:00
/** Reloads all tabs, bypassing the cache if hard is set to true */
//#background
export async function reloadall ( hard = false ) {
let tabs = await browser . tabs . query ( { } )
let reloadprops = { bypassCache : hard }
tabs . map ( tab = > browser . tabs . reload ( tab . id , reloadprops ) )
}
2017-10-12 04:02:01 +01:00
/** Reload the next n tabs, starting with activeTab. bypass cache for all */
//#background
export async function reloadhard ( n = 1 ) {
reload ( n , true )
}
2017-11-19 06:05:15 +00:00
/ * * O p e n a n e w p a g e i n t h e c u r r e n t t a b .
@param urlarr
- if first word looks like it has a schema , treat as a URI
- else if the first word contains a dot , treat as a domain name
- else if the first word is a key of [ [ SEARCH_URLS ] ] , treat all following terms as search parameters for that provider
- else treat as search parameters for google
2017-11-29 20:13:40 +00:00
Related settings :
"searchengine" : "google" or any of [ [ SEARCH_URLS ] ]
2017-11-19 06:05:15 +00:00
* /
2017-10-12 04:02:01 +01:00
//#content
2017-10-22 16:33:43 +01:00
export function open ( . . . urlarr : string [ ] ) {
2017-11-09 15:30:09 +00:00
let url = urlarr . join ( " " )
2017-10-12 04:02:01 +01:00
window . location . href = forceURI ( url )
}
2017-11-19 06:05:15 +00:00
/ * * S h o w t h i s p a g e .
` :help <excmd> ` jumps to the entry for that command .
2017-10-23 23:44:07 +01:00
2017-11-19 06:05:15 +00:00
e . g . ` :help bind `
* /
//#background
export async function help ( excmd? : string ) {
const docpage = browser . extension . getURL ( "static/docs/modules/_excmds_.html#" )
if ( excmd === undefined ) excmd = "tridactyl-help-page"
if ( ( await activeTab ( ) ) . url . startsWith ( docpage ) ) {
open ( docpage + excmd )
} else {
tabopen ( docpage + excmd )
}
2017-10-23 23:44:07 +01:00
}
2017-11-03 19:10:12 +00:00
/** @hidden */
2017-11-22 22:21:46 +00:00
// Find clickable next-page/previous-page links whose text matches the supplied pattern,
// and return the last such link.
//
// If no matching link is found, return null.
//
// We return the last link that matches because next/prev buttons tend to be at the end of the page
// whereas lots of blogs have "VIEW MORE" etc. plastered all over their pages.
function findRelLink ( pattern : RegExp ) : HTMLAnchorElement | null {
const links = < NodeListOf < HTMLAnchorElement > > document . querySelectorAll ( 'a[href]' )
let lastLink = null
for ( const link of links ) {
// `innerText` gives better (i.e. less surprising) results than `textContent`
// at the expense of being much slower, but that shouldn't be an issue here
// as it's a one-off operation that's only performed when we're leaving a page
if ( pattern . test ( link . innerText ) ) {
lastLink = link
}
}
return lastLink
2017-10-12 04:02:01 +01:00
}
2017-11-22 22:21:46 +00:00
/** @hidden */
// Return the last element in the document matching the supplied selector,
// or null if there are no matches.
function selectLast ( selector : string ) : HTMLElement | null {
const nodes = < NodeListOf < HTMLElement > > document . querySelectorAll ( selector )
return nodes . length ? nodes [ nodes . length - 1 ] : null
}
/ * * F i n d a l i k e l y n e x t / p r e v i o u s l i n k a n d f o l l o w i t
*
* @param rel the relation of the target page to the current page : "next" or "prev"
* /
2017-10-12 04:02:01 +01:00
//#content
2017-11-22 22:21:46 +00:00
export function followpage ( rel : 'next' | 'prev' = 'next' ) {
const link = < HTMLLinkElement > selectLast ( ` link[rel~= ${ rel } ][href] ` )
2017-10-12 04:02:01 +01:00
2017-11-22 22:21:46 +00:00
if ( link ) {
window . location . href = link . href
return
}
2017-10-12 04:02:01 +01:00
2017-11-22 22:21:46 +00:00
const anchor = < HTMLAnchorElement > selectLast ( ` a[rel~= ${ rel } ][href] ` ) ||
findRelLink ( REL_PATTERN [ rel ] )
2017-10-12 04:02:01 +01:00
2017-11-22 22:21:46 +00:00
if ( anchor ) {
anchor . click ( )
}
2017-10-12 04:02:01 +01:00
}
2017-11-15 12:40:26 +00:00
2017-11-18 16:52:23 +00:00
/ * * I n c r e m e n t t h e c u r r e n t t a b U R L
*
* @param count the increment step , can be positive or negative
* /
//#content
2017-11-21 10:58:00 +00:00
export function urlincrement ( count = 1 ) {
2017-11-18 16:52:23 +00:00
let newUrl = incrementUrl ( window . location . href , count )
if ( newUrl !== null ) {
window . location . href = newUrl
}
}
2017-11-19 16:55:18 +00:00
/ * * G o t o t h e r o o t d o m a i n o f t h e c u r r e n t U R L
* /
//#content
export function urlroot ( ) {
let rootUrl = getUrlRoot ( window . location )
if ( rootUrl !== null ) {
window . location . href = rootUrl . href
}
}
2017-11-21 10:52:30 +00:00
/ * * G o t o t h e p a r e n t U R L o f t h e c u r r e n t t a b ' s U R L
* /
//#content
export function urlparent ( ) {
let parentUrl = getUrlParent ( window . location )
if ( parentUrl !== null ) {
window . location . href = parentUrl . href
}
}
2017-11-27 19:48:49 +01:00
/ * * R e t u r n s t h e u r l o f l i n k s t h a t h a v e a m a t c h i n g r e l .
2017-11-28 00:05:12 +00:00
Don 't bind to this: it' s an internal function .
@hidden
2017-11-27 19:48:49 +01:00
* /
//#content
export function geturlsforlinks ( rel : string ) {
let elems = document . querySelectorAll ( "link[rel='" + rel + "']" ) as NodeListOf < HTMLLinkElement >
console . log ( rel , elems )
if ( elems )
return Array . prototype . map . call ( elems , x = > x . href )
return [ ]
}
2017-11-15 12:40:26 +00:00
//#background
export function zoom ( level = 0 ) {
level = level > 3 ? level / 100 : level
browser . tabs . setZoom ( level )
}
2017-11-21 19:10:42 +00:00
//#background
export async function reader() {
if ( await l ( firefoxVersionAtLeast ( 58 ) ) ) {
2017-11-29 00:36:53 +00:00
let aTab = await activeTab ( )
if ( aTab . isArticle ) {
browser . tabs . toggleReaderMode ( )
} // else {
// // once a statusbar exists an error can be displayed there
// }
2017-11-21 19:10:42 +00:00
}
}
2017-11-23 23:57:04 +00:00
/ * * T h e k i n d s o f i n p u t e l e m e n t s t h a t w e w a n t t o b e i n c l u d e d i n t h e " f o c u s i n p u t "
* command ( gi )
* /
export const INPUTTAGS_selectors = `
input :not ( [ disabled ] ) : not ( [ readonly ] ) : - moz - any (
: not ( [ type ] ) ,
[ type = 'text' ] ,
[ type = 'search' ] ,
[ type = 'password' ] ,
[ type = 'datetime' ] ,
[ type = 'datetime-local' ] ,
[ type = 'date' ] ,
[ type = 'month' ] ,
[ type = 'time' ] ,
[ type = 'week' ] ,
[ type = 'number' ] ,
[ type = 'range' ] ,
[ type = 'email' ] ,
[ type = 'url' ] ,
[ type = 'tel' ] ,
[ type = 'color' ]
) ,
textarea :not ( [ disabled ] ) : not ( [ readonly ] ) ,
object ,
[ role = 'application' ]
`
/** Password field selectors */
const INPUTPASSWORD_selectors = `
input [ type = 'password' ]
`
/ * * D O M r e f e r e n c e t o t h e l a s t u s e d I n p u t f i e l d
* /
2017-11-26 12:18:06 +00:00
//#content_helper
let LAST_USED_INPUT : HTMLElement = null
2017-11-23 23:57:04 +00:00
/ * * F o c u s t h e l a s t u s e d i n p u t o n t h e p a g e
*
* @param nth focus the nth input on the page , or "special" inputs :
* "-l" : last focussed input
* "-p" : first password field
* "-b" : biggest input field
* /
//#content
export function focusinput ( nth : number | string ) {
let inputToFocus : HTMLElement = null
// set to false to avoid falling back on the first available input
// if a special finder fails
let fallbackToNumeric = true
// nth = "-l" -> use the last used input for this page
if ( nth === "-l" ) {
// try to recover the last used input stored as a
// DOM node, which should be exactly the one used before (or null)
2017-11-26 12:18:06 +00:00
inputToFocus = LAST_USED_INPUT
2017-11-23 23:57:04 +00:00
// failed to find that? - look up in sessionStorage?
// will need to serialise the last used input to a string that
// we can look up in future (tabindex, selector?), perhaps along with
// a way to remember the page it was on?
}
else if ( nth === "-p" ) {
// attempt to find a password input
fallbackToNumeric = false
let inputs = DOM . getElemsBySelector ( INPUTPASSWORD_selectors ,
2017-11-28 23:20:42 +00:00
[ DOM . isSubstantial ] )
2017-11-23 23:57:04 +00:00
if ( inputs . length ) {
inputToFocus = < HTMLElement > inputs [ 0 ]
}
}
else if ( nth === "-b" ) {
let inputs = DOM . getElemsBySelector ( INPUTTAGS_selectors ,
2017-11-28 23:20:42 +00:00
[ DOM . isSubstantial ] ) as HTMLElement [ ]
2017-11-23 23:57:04 +00:00
inputToFocus = inputs . sort ( DOM . compareElementArea ) . slice ( - 1 ) [ 0 ]
}
// either a number (not special) or we failed to find a special input when
// asked and falling back is acceptable
if ( ! inputToFocus && fallbackToNumeric ) {
let index = isNaN ( < number > nth ) ? 0 : < number > nth
inputToFocus = DOM . getNthElement ( INPUTTAGS_selectors ,
2017-11-28 23:20:42 +00:00
index , [ DOM . isSubstantial ] )
2017-11-23 23:57:04 +00:00
}
2017-11-26 12:18:06 +00:00
if ( inputToFocus ) inputToFocus . focus ( )
2017-11-23 23:57:04 +00:00
}
2017-11-26 12:18:06 +00:00
// Store the last focused element
//#content_helper
2017-11-26 12:27:54 +00:00
document . addEventListener ( "focusin" , e = > { if ( DOM . isTextEditable ( e . target as HTMLElement ) ) LAST_USED_INPUT = e . target as HTMLElement } )
2017-11-26 12:18:06 +00:00
2017-10-12 04:02:01 +01:00
// }}}
// {{{ TABS
2017-11-27 22:42:50 +11:00
/ * * S w i t c h t o t h e t a b b y i n d e x ( p o s i t i o n o n t a b b a r ) , w r a p p i n g r o u n d .
2017-11-28 00:01:41 +00:00
@param index
1 - based index of the tab to target . Wraps such that 0 = last tab , - 1 =
penultimate tab , etc .
if undefined , return activeTabId ( )
* /
2017-11-27 17:27:17 +11:00
/** @hidden */
//#background_helper
2017-11-28 00:01:41 +00:00
async function tabIndexSetActive ( index : number ) {
tabSetActive ( await idFromIndex ( index ) )
2017-10-12 04:02:01 +01:00
}
2017-11-27 17:27:17 +11:00
/ * * S w i t c h t o t h e n e x t t a b , w r a p p i n g r o u n d .
If increment is specified , move that many tabs forwards .
* /
//#background
export async function tabnext ( increment = 1 ) {
2017-11-28 00:01:41 +00:00
tabIndexSetActive ( ( await activeTab ( ) ) . index + increment + 1 )
2017-11-27 17:27:17 +11:00
}
2017-11-28 16:16:41 +00:00
/ * * S w i t c h t o t h e n e x t t a b , w r a p p i n g r o u n d .
If an index is specified , go to the tab with that number ( this mimics the
behaviour of ` {count}gt ` in vim , except that this command will accept a
count that is out of bounds ( and will mod it so that it is within bounds as
per [ [ tabmove ] ] , etc ) ) .
* /
//#background
export async function tabnext_gt ( index? : number ) {
if ( index === undefined ) {
tabnext ( )
} else {
tabIndexSetActive ( index )
}
}
2017-11-27 17:27:17 +11:00
/ * * S w i t c h t o t h e p r e v i o u s t a b , w r a p p i n g r o u n d .
If increment is specified , move that many tabs backwards .
* /
//#background
export async function tabprev ( increment = 1 ) {
2017-11-28 00:01:41 +00:00
tabIndexSetActive ( ( await activeTab ( ) ) . index - increment + 1 )
2017-11-27 17:27:17 +11:00
}
/** Switch to the first tab. */
//#background
export async function tabfirst() {
2017-11-28 00:01:41 +00:00
tabIndexSetActive ( 1 )
2017-11-27 17:27:17 +11:00
}
/** Switch to the last tab. */
2017-10-12 04:02:01 +01:00
//#background
2017-11-27 17:27:17 +11:00
export async function tablast() {
2017-11-28 00:01:41 +00:00
tabIndexSetActive ( 0 )
2017-10-12 04:02:01 +01:00
}
2017-11-19 06:05:15 +00:00
/** Like [[open]], but in a new tab */
2017-10-12 04:02:01 +01:00
//#background
2017-10-24 17:40:00 +01:00
export async function tabopen ( . . . addressarr : string [ ] ) {
2017-10-23 09:42:50 +01:00
let uri
2017-11-09 15:30:09 +00:00
let address = addressarr . join ( ' ' )
2017-10-24 17:40:00 +01:00
if ( address != "" ) uri = forceURI ( address )
2017-10-23 09:42:50 +01:00
browser . tabs . create ( { url : uri } )
2017-10-12 04:02:01 +01:00
}
2017-11-28 00:01:41 +00:00
/ * * R e s o l v e a t a b i n d e x t o t h e t a b i d o f t h e c o r r e s p o n d i n g t a b i n t h i s w i n d o w .
@param index
1 - based index of the tab to target . Wraps such that 0 = last tab , - 1 =
penultimate tab , etc .
if undefined , return activeTabId ( )
@hidden
* /
//#background_helper
async function idFromIndex ( index? : number ) : Promise < number > {
2017-11-28 13:53:22 +00:00
if ( index !== undefined ) {
2017-11-28 15:47:12 +00:00
// Wrap
index = ( index - 1 ) . mod (
( await l ( browser . tabs . query ( { currentWindow : true } ) ) ) . length )
+ 1
2017-11-28 00:01:41 +00:00
// Return id of tab with that index.
2017-11-28 15:47:12 +00:00
return ( await l ( browser . tabs . query ( {
2017-11-28 00:01:41 +00:00
currentWindow : true ,
index : index - 1 ,
2017-11-28 15:47:12 +00:00
} ) ) ) [ 0 ] . id
2017-11-28 00:01:41 +00:00
} else {
return await activeTabId ( )
}
}
2017-11-29 00:36:53 +00:00
/** Close all other tabs in this window */
2017-11-29 02:46:11 +08:00
//#background
export async function tabonly() {
2017-11-29 00:36:53 +00:00
const tabs = await browser . tabs . query ( {
pinned : false ,
active : false ,
currentWindow : true
2017-11-29 07:19:02 +08:00
} )
2017-11-29 00:36:53 +00:00
const tabsIds = tabs . map ( tab = > tab . id )
2017-11-29 02:46:11 +08:00
browser . tabs . remove ( tabsIds )
}
2017-11-28 00:01:41 +00:00
/ * * D u p l i c a t e a t a b .
@param index
The 1 - based index of the tab to target . index < 1 wraps . If omitted , this tab .
* /
2017-10-12 04:02:01 +01:00
//#background
2017-11-28 00:01:41 +00:00
export async function tabduplicate ( index? : number ) {
browser . tabs . duplicate ( await idFromIndex ( index ) )
2017-10-12 04:02:01 +01:00
}
2017-11-28 00:01:41 +00:00
/ * * D e t a c h a t a b , o p e n i n g i t i n a n e w w i n d o w .
@param index
The 1 - based index of the tab to target . index < 1 wraps . If omitted , this tab .
* /
2017-10-12 04:02:01 +01:00
//#background
2017-11-28 00:01:41 +00:00
export async function tabdetach ( index? : number ) {
browser . windows . create ( { tabId : await idFromIndex ( index ) } )
2017-10-12 04:02:01 +01:00
}
2017-11-28 00:01:41 +00:00
/ * * C l o s e a t a b .
Known bug : autocompletion will make it impossible to close more than one tab at once if the list of numbers looks enough like an open tab ' s title or URL .
@param indexes
The 1 - based indexes of the tabs to target . indexes < 1 wrap . If omitted , this tab .
* /
2017-10-12 04:02:01 +01:00
//#background
2017-11-28 00:01:41 +00:00
export async function tabclose ( . . . indexes : string [ ] ) {
if ( indexes . length > 0 ) {
const idsPromise = indexes . map ( index = > idFromIndex ( Number ( index ) ) )
browser . tabs . remove ( await Promise . all ( idsPromise ) )
2017-10-12 04:02:01 +01:00
} else {
2017-11-28 00:01:41 +00:00
// Close current tab
2017-11-09 00:41:07 +00:00
browser . tabs . remove ( await activeTabId ( ) )
2017-10-12 04:02:01 +01:00
}
}
2017-10-21 12:54:48 +02:00
/** restore most recently closed tab in this window unless the most recently closed item was a window */
2017-10-12 04:02:01 +01:00
//#background
export async function undo ( ) {
2017-10-21 12:54:48 +02:00
const current_win_id : number = ( await browser . windows . getCurrent ( ) ) . id
const sessions = await browser . sessions . getRecentlyClosed ( )
// The first session object that's a window or a tab from this window. Or undefined if sessions is empty.
let closed = sessions . find ( ( s ) = > {
2017-10-22 16:32:11 +01:00
return ( 'window' in s || s . tab && ( s . tab . windowId == current_win_id ) )
2017-10-21 12:54:48 +02:00
} )
if ( closed ) {
if ( closed . tab ) {
browser . sessions . restore ( closed . tab . sessionId )
}
else if ( closed . window ) {
browser . sessions . restore ( closed . window . sessionId )
}
}
2017-10-12 04:02:01 +01:00
}
2017-11-28 00:01:41 +00:00
/ * * M o v e t h e c u r r e n t t a b t o b e j u s t i n f r o n t o f t h e i n d e x s p e c i f i e d .
Known bug : This supports relative movement , but autocomple doesn ' t know
that yet and will override positive and negative indexes .
Put a space in front of tabmove if you want to disable completion and have
the relative indexes at the command line .
Binds are unaffected .
@param index
New index for the current tab .
1 is the first index . 0 is the last index . - 1 is the penultimate , etc .
* /
2017-10-12 04:02:01 +01:00
//#background
2017-11-28 00:01:41 +00:00
export async function tabmove ( index = "0" ) {
const aTab = await activeTab ( )
let newindex : number
if ( index . startsWith ( "+" ) || index . startsWith ( "-" ) ) {
newindex = Math . max ( 0 , Number ( index ) + aTab . index )
} else newindex = Number ( index ) - 1
browser . tabs . move ( aTab . id , { index : newindex } )
2017-10-12 04:02:01 +01:00
}
2017-11-19 06:05:15 +00:00
/** Pin the current tab */
2017-10-12 04:02:01 +01:00
//#background
export async function pin() {
let aTab = await activeTab ( )
browser . tabs . update ( aTab . id , { pinned : ! aTab . pinned } )
}
// }}}
// {{{ WINDOWS
2017-11-19 06:05:15 +00:00
/** Like [[open]], but in a new window */
2017-10-12 04:02:01 +01:00
//#background
export async function winopen ( . . . args : string [ ] ) {
let address : string
const createData = { }
if ( args [ 0 ] === "-private" ) {
createData [ "incognito" ] = true
2017-11-09 15:30:09 +00:00
address = args . slice ( 1 , args . length ) . join ( ' ' )
} else address = args . join ( ' ' )
2017-11-14 12:23:01 +00:00
createData [ "url" ] = address != "" ? forceURI ( address ) : null
2017-10-12 04:02:01 +01:00
browser . windows . create ( createData )
}
//#background
export async function winclose() {
browser . windows . remove ( ( await browser . windows . getCurrent ( ) ) . id )
}
2017-11-02 19:36:44 +00:00
2017-11-19 06:05:15 +00:00
/** Close all windows */
2017-11-02 19:36:44 +00:00
// It's unclear if this will leave a session that can be restored.
// We might have to do it ourselves.
//#background
export async function qall ( ) {
let windows = await browser . windows . getAll ( )
windows . map ( ( window ) = > browser . windows . remove ( window . id ) )
}
2017-10-12 04:02:01 +01:00
// }}}
// {{{ MISC
2017-11-19 06:05:15 +00:00
/** Deprecated */
2017-10-23 09:42:50 +01:00
//#background
export function suppress ( preventDefault? : boolean , stopPropagation? : boolean ) {
2017-11-19 03:22:59 +00:00
mode ( "ignore" )
2017-10-23 09:42:50 +01:00
}
2017-11-26 00:06:02 +00:00
//#background
export function version ( ) {
2017-11-28 17:23:16 +00:00
clipboard ( "yank" , "REPLACE_ME_WITH_THE_VERSION_USING_SED" )
fillcmdline_notrail ( "REPLACE_ME_WITH_THE_VERSION_USING_SED" )
2017-11-26 00:06:02 +00:00
}
2017-11-19 06:05:15 +00:00
/ * * E x a m p l e :
- ` mode ignore ` to ignore all keys .
* /
2017-10-12 04:02:01 +01:00
//#background
2017-11-09 00:41:07 +00:00
export function mode ( mode : ModeName ) {
2017-11-19 06:05:15 +00:00
// TODO: event emition on mode change.
2017-11-19 07:57:30 +00:00
if ( mode === "hint" ) {
2017-11-19 06:05:15 +00:00
hint ( )
} else {
state . mode = mode
}
2017-10-12 04:02:01 +01:00
}
2017-11-19 06:05:15 +00:00
//#background_helper
async function getnexttabs ( tabid : number , n? : number ) {
2017-10-12 04:02:01 +01:00
const curIndex : number = ( await browser . tabs . get ( tabid ) ) . index
const tabs : browser.tabs.Tab [ ] = await browser . tabs . query ( {
currentWindow : true ,
} )
const indexFilter = ( ( tab : browser.tabs.Tab ) = > {
return (
curIndex <= tab . index &&
( n ? tab . index < curIndex + Number ( n ) : true )
)
} ) . bind ( n )
return tabs . filter ( indexFilter ) . map ( ( tab : browser.tabs.Tab ) = > {
return tab . id
} )
}
// Moderately slow; should load in results as they arrive, perhaps
// Todo: allow jumping to buffers once they are found
// Consider adding to buffers with incremental search
// maybe only if no other results in URL etc?
// Find out how to return context of each result
//#background
/* export async function findintabs(query: string) { */
/* const tabs = await browser.tabs.query({currentWindow: true}) */
/* console.log(query) */
/* const findintab = async tab => */
/* await browser.find.find(query, {tabId: tab.id}) */
/* let results = [] */
/* for (let tab of tabs) { */
/* let result = await findintab(tab) */
/* if (result.count > 0) { */
/* results.push({tab, result}) */
/* } */
/* } */
/* results.sort(r => r.result.count) */
/* console.log(results) */
/* return results */
/* } */
// }}}
// {{{ CMDLINE
2017-11-18 13:47:10 +00:00
//#background_helper
import * as controller from './controller'
2017-11-19 06:05:15 +00:00
/ * * S p l i t ` c m d s ` o n p i p e s ( | ) a n d t r e a t e a c h a s i t ' s o w n c o m m a n d .
2017-11-18 13:47:10 +00:00
2017-11-19 06:05:15 +00:00
Workaround : this should clearly be in the parser , but we haven ' t come up with a good way to deal with | s in URLs , search terms , etc . yet .
2017-11-18 13:47:10 +00:00
* /
//#background
export function composite ( . . . cmds : string [ ] ) {
cmds = cmds . join ( " " ) . split ( "|" )
cmds . forEach ( controller . acceptExCmd )
}
2017-11-22 18:05:54 +00:00
/** Please use fillcmdline instead */
//#background
2017-10-12 04:02:01 +01:00
export function showcmdline() {
2017-11-22 18:05:54 +00:00
CommandLineBackground . show ( )
2017-10-12 04:02:01 +01:00
}
2017-11-19 06:05:15 +00:00
/** Set the current value of the commandline to string *with* a trailing space */
2017-10-12 04:02:01 +01:00
//#background
2017-10-24 12:51:04 +01:00
export function fillcmdline ( . . . strarr : string [ ] ) {
let str = strarr . join ( " " )
2017-10-12 04:02:01 +01:00
showcmdline ( )
2017-10-28 05:11:10 +01:00
messageActiveTab ( "commandline_frame" , "fillcmdline" , [ str ] )
2017-10-12 04:02:01 +01:00
}
2017-11-19 06:05:15 +00:00
/** Set the current value of the commandline to string *without* a trailing space */
2017-11-09 15:30:09 +00:00
//#background
export function fillcmdline_notrail ( . . . strarr : string [ ] ) {
let str = strarr . join ( " " )
let trailspace = false
showcmdline ( )
messageActiveTab ( "commandline_frame" , "fillcmdline" , [ str , trailspace ] )
}
2017-11-19 06:05:15 +00:00
/ * * E q u i v a l e n t t o ` f i l l c m d l i n e _ n o t r a i l < y o u r a r g s > < c u r r e n t U R L > `
See also [ [ fillcmdline_notrail ] ]
* /
2017-11-09 15:30:09 +00:00
//#background
export async function current_url ( . . . strarr : string [ ] ) {
2017-11-09 15:30:09 +00:00
fillcmdline_notrail ( . . . strarr , ( await activeTab ( ) ) . url )
2017-11-09 15:30:09 +00:00
}
2017-11-19 06:05:15 +00:00
/ * * U s e t h e s y s t e m c l i p b o a r d .
If ` excmd == "open" ` , call [ [ open ] ] with the contents of the clipboard . Similarly for [ [ tabopen ] ] .
If ` excmd == "yank" ` , copy the current URL , or if given , the value of toYank , into the system clipboard .
2017-11-27 19:15:04 +00:00
If ` excmd == "yankcanon" ` , copy the canonical URL of the current page if it exists , otherwise copy the current URL .
If ` excmd == "yankshort" ` , copy the shortlink version of the current URL , and fall back to the canonical then actual URL . Known to work on https : //yankshort.neocities.org/.
2017-11-19 06:05:15 +00:00
Unfortunately , javascript can only give us the ` clipboard ` clipboard , not e . g . the X selection clipboard .
* /
2017-10-28 19:20:31 +08:00
//#background
2017-11-27 19:48:49 +01:00
export async function clipboard ( excmd : "open" | "yank" | "yankshort" | "yankcanon" | "tabopen" = "open" , . . . toYank : string [ ] ) {
2017-11-19 06:05:15 +00:00
let content = toYank . join ( " " )
2017-11-16 19:58:33 +00:00
let url = ""
2017-11-27 19:48:49 +01:00
let urls = [ ]
2017-10-28 19:20:31 +08:00
switch ( excmd ) {
2017-11-27 19:48:49 +01:00
case 'yankshort' :
2017-11-28 00:05:12 +00:00
urls = await geturlsforlinks ( "shortlink" )
2017-11-27 19:48:49 +01:00
if ( urls . length > 0 ) {
messageActiveTab ( "commandline_frame" , "setClipboard" , [ urls [ 0 ] ] )
break
}
case 'yankcanon' :
2017-11-28 00:05:12 +00:00
urls = await geturlsforlinks ( "canonical" )
2017-11-27 19:48:49 +01:00
if ( urls . length > 0 ) {
messageActiveTab ( "commandline_frame" , "setClipboard" , [ urls [ 0 ] ] )
break
}
2017-10-28 19:20:31 +08:00
case 'yank' :
2017-10-28 13:42:54 +01:00
await messageActiveTab ( "commandline_content" , "focus" )
2017-11-04 17:30:34 +00:00
content = ( content == "" ) ? ( await activeTab ( ) ) . url : content
messageActiveTab ( "commandline_frame" , "setClipboard" , [ content ] )
2017-10-28 19:20:31 +08:00
break
case 'open' :
2017-10-28 13:42:54 +01:00
await messageActiveTab ( "commandline_content" , "focus" )
2017-11-16 19:58:33 +00:00
url = await messageActiveTab ( "commandline_frame" , "getClipboard" )
url && open ( url )
break
case 'tabopen' :
await messageActiveTab ( "commandline_content" , "focus" )
url = await messageActiveTab ( "commandline_frame" , "getClipboard" )
url && tabopen ( url )
2017-10-28 19:20:31 +08:00
break
default :
// todo: maybe we should have some common error and error handler
throw new Error ( ` [clipboard] unknown excmd: ${ excmd } ` )
2017-10-19 20:15:01 +01:00
}
2017-11-22 18:05:54 +00:00
CommandLineBackground . hide ( )
2017-10-19 20:15:01 +01:00
}
2017-10-12 04:02:01 +01:00
// {{{ Buffer/completion stuff
2017-11-28 00:20:23 +00:00
/ * * E q u i v a l e n t t o ` f i l l c m d l i n e b u f f e r `
Sort of Vimperator alias
* /
2017-10-12 04:02:01 +01:00
//#background
2017-11-15 13:41:04 -08:00
export async function tabs() {
2017-10-12 04:02:01 +01:00
fillcmdline ( "buffer" )
2017-11-15 13:41:04 -08:00
}
2017-11-28 00:20:23 +00:00
/ * * E q u i v a l e n t t o ` f i l l c m d l i n e b u f f e r `
Sort of Vimperator alias
* /
2017-11-15 13:41:04 -08:00
//#background
export async function buffers() {
tabs ( )
2017-10-12 04:02:01 +01:00
}
2017-11-27 22:42:50 +11:00
/ * * C h a n g e a c t i v e t a b .
2017-11-27 19:54:45 +00:00
@param index
Starts at 1 . 0 refers to last tab , - 1 to penultimate tab , etc .
"#" means the tab that was last accessed in this window
2017-11-27 22:42:50 +11:00
* /
2017-10-12 04:02:01 +01:00
//#background
2017-11-27 19:54:45 +00:00
export async function buffer ( index : number | '#' ) {
if ( index === "#" ) {
2017-11-27 22:42:50 +11:00
// Switch to the most recently accessed buffer
2017-11-28 00:01:41 +00:00
tabIndexSetActive (
2017-10-12 04:02:01 +01:00
( await browser . tabs . query ( { currentWindow : true } ) ) . sort ( ( a , b ) = > {
return a . lastAccessed < b . lastAccessed ? 1 : - 1
2017-11-28 00:01:41 +00:00
} ) [ 1 ] . index + 1
2017-11-07 12:02:24 +00:00
)
2017-11-27 19:54:45 +00:00
} else if ( Number . isInteger ( Number ( index ) ) ) {
2017-11-28 00:01:41 +00:00
tabIndexSetActive ( Number ( index ) )
2017-11-27 22:42:50 +11:00
}
2017-10-12 04:02:01 +01:00
}
// }}}
// }}}
2017-11-05 14:10:11 +00:00
2017-11-09 00:41:07 +00:00
// {{{ SETTINGS
2017-11-19 06:05:15 +00:00
/ * * B i n d a s e q u e n c e o f k e y s t o a n e x c m d .
This is an easier - to - implement bodge while we work on vim - style maps .
Examples :
- ` bind G fillcmdline tabopen google `
2017-11-26 14:56:09 +00:00
- ` bind D composite tabclose | buffer # `
2017-11-19 06:05:15 +00:00
- ` bind j scrollline 20 `
- ` bind F hint -b `
Use [ [ composite ] ] if you want to execute multiple excmds . Use
[ [ fillcmdline ] ] to put a string in the cmdline and focus the cmdline
( otherwise the string is executed immediately ) .
See also :
- [ [ unbind ] ]
- [ [ reset ] ]
* /
2017-11-05 14:10:11 +00:00
//#background
2017-11-29 19:51:18 +00:00
export function bind ( key : string , . . . bindarr : string [ ] ) {
2017-11-09 12:13:08 +00:00
let exstring = bindarr . join ( " " )
2017-11-29 19:51:18 +00:00
config . set ( "nmaps" , exstring , key )
2017-11-05 14:10:11 +00:00
}
2017-11-19 06:05:15 +00:00
/ * * U n b i n d a s e q u e n c e o f k e y s s o t h a t t h e y d o n o t h i n g a t a l l .
See also :
- [ [ bind ] ]
- [ [ reset ] ]
* /
2017-11-05 14:10:11 +00:00
//#background
export async function unbind ( key : string ) {
2017-11-19 06:05:15 +00:00
bind ( key , "" )
2017-11-05 14:10:11 +00:00
}
2017-11-19 06:05:15 +00:00
/ * * R e s t o r e s a s e q u e n c e o f k e y s t o t h e i r d e f a u l t v a l u e .
See also :
- [ [ bind ] ]
- [ [ unbind ] ]
* /
2017-11-05 14:10:11 +00:00
//#background
export async function reset ( key : string ) {
2017-11-29 19:51:18 +00:00
config . unset ( "nmaps" , key )
// Code for dealing with legacy binds
2017-11-05 14:10:11 +00:00
let nmaps = ( await browser . storage . sync . get ( "nmaps" ) ) [ "nmaps" ]
nmaps = ( nmaps == undefined ) ? { } : nmaps
delete nmaps [ key ]
browser . storage . sync . set ( { nmaps } )
}
2017-11-20 23:32:24 +00:00
/ * * B i n d a q u i c k m a r k f o r t h e c u r r e n t U R L t o a k e y .
Afterwards use go [ key ] , gn [ key ] , or gw [ key ] to [ [ open ] ] , [ [ tabopen ] ] , or
[ [ winopen ] ] the URL respectively .
* /
2017-11-19 13:45:18 +01:00
//#background
export async function quickmark ( key : string ) {
// ensure we're binding to a single key
if ( key . length !== 1 ) {
return
}
let address = ( await activeTab ( ) ) . url
2017-11-21 03:30:16 +00:00
// Have to await these or they race!
await bind ( "gn" + key , "tabopen" , address )
await bind ( "go" + key , "open" , address )
await bind ( "gw" + key , "winopen" , address )
2017-11-19 13:45:18 +01:00
}
2017-11-29 16:28:06 +00:00
//#background
export function get ( target : string , property? : string ) {
console . log ( config . get ( target , property ) )
}
2017-11-29 20:13:40 +00:00
/ * * S e t a s e t t i n g t o a v a l u e
Currently , this only supports string settings without any whitespace
( i . e . not nmaps . )
It can be used on any string < - > string settings found [ here ] ( / s t a t i c / d o c s / m o d u l e s / _ c o n f i g _ . h t m l # d e f a u l t s )
* /
2017-11-29 16:28:06 +00:00
//#background
2017-11-29 21:38:30 +00:00
export function set ( setting : string , value? : string ) {
2017-11-29 16:56:56 +00:00
// We don't support setting objects yet
2017-11-29 21:38:30 +00:00
if ( setting != "nmaps" ) {
if ( value !== undefined ) {
config . set ( setting , value )
} else fillcmdline_notrail ( "set " + setting + " " + config . get ( setting ) )
}
2017-11-29 16:28:06 +00:00
}
2017-11-29 18:57:04 +00:00
//#background
export function unset ( target : string , property? : string ) {
config . unset ( target , property )
}
2017-11-29 19:51:18 +00:00
// not required as we automatically save all config
////#background
//export function saveconfig(){
// config.save(config.get("storageloc"))
//}
2017-11-29 16:56:56 +00:00
2017-11-29 19:51:18 +00:00
////#background
//export function mktridactylrc(){
// saveconfig()
//}
2017-11-29 16:56:56 +00:00
2017-11-09 00:41:07 +00:00
// }}}
// {{{ HINTMODE
//#background_helper
2017-11-18 01:51:46 +00:00
import * as hinting from './hinting_background'
2017-11-09 00:41:07 +00:00
2017-11-24 13:01:44 +00:00
/ * * H i n t a p a g e .
2017-11-28 02:12:07 +00:00
@param option
- - b open in background
- - y copy ( yank ) link ' s target to clipboard
- - p copy an element ' s text to the clipboard
2017-11-30 04:11:49 +00:00
- - r read an element ' s text with text - to - speech
2017-11-28 02:12:07 +00:00
- - i view an image
- - I view an image in a new tab
- - ; focus an element
2017-11-28 22:51:53 +00:00
- - # yank an element ' s anchor URL to clipboard
2017-11-29 13:23:30 +00:00
- - c [ selector ] hint links that match the css selector
- ` bind ;c hint -c [class*="expand"],[class="togg"] ` works particularly well on reddit and HN
2017-11-29 20:13:40 +00:00
Related settings :
"hintchars" : "hjklasdfgyuiopqwertnmzxcvb"
"hintorder" : "normal" or "reverse"
2017-11-28 02:12:07 +00:00
* /
2017-11-09 00:41:07 +00:00
//#background
2017-11-29 13:23:30 +00:00
export function hint ( option? : string , selectors = "" ) {
2017-11-18 01:51:46 +00:00
if ( option === '-b' ) hinting . hintPageOpenInBackground ( )
2017-11-22 19:09:52 +00:00
else if ( option === "-y" ) hinting . hintPageYank ( )
2017-11-22 20:47:35 +00:00
else if ( option === "-p" ) hinting . hintPageTextYank ( )
2017-11-22 20:38:02 +00:00
else if ( option === "-i" ) hinting . hintImage ( false )
else if ( option === "-I" ) hinting . hintImage ( true )
2017-11-24 13:01:44 +00:00
else if ( option === "-;" ) hinting . hintFocus ( )
2017-11-28 22:51:53 +00:00
else if ( option === "-#" ) hinting . hintPageAnchorYank ( )
2017-11-29 13:23:30 +00:00
else if ( option === "-c" ) hinting . hintPageSimple ( selectors )
2017-11-30 04:11:49 +00:00
else if ( option === "-r" ) hinting . hintRead ( )
2017-11-18 01:51:46 +00:00
else hinting . hintPageSimple ( )
2017-11-09 00:41:07 +00:00
}
2017-11-21 20:28:48 +00:00
2017-11-09 00:41:07 +00:00
// }}}
2017-11-19 13:45:18 +01:00
// {{{ GOBBLE mode
//#background_helper
import * as gobbleMode from './parsers/gobblemode'
2017-11-20 23:32:24 +00:00
/ * * I n i t i a l i z e g o b b l e m o d e .
It will read ` nChars ` input keys , append them to ` endCmd ` and execute that
string .
* /
2017-11-19 13:45:18 +01:00
//#background
export async function gobble ( nChars : number , endCmd : string ) {
gobbleMode . init ( nChars , endCmd )
}
// }}}
2017-11-22 11:54:17 +00:00
//
2017-11-30 04:11:49 +00:00
// {{{TEXT TO SPEECH
import * as TTS from './text_to_speech'
/ * *
* Read text content of elements matching the given selector
*
* @param selector the selector to match elements
* /
//#content_helper
function tssReadFromCss ( selector : string ) : void {
let elems = DOM . getElemsBySelector ( selector , [ ] )
elems . forEach ( e = > {
TTS . readText ( e . textContent )
} )
}
/ * *
* Read the given text using the browser ' s text to speech functionality and
* the settings currently set
*
* @param mode the command mode
* - t read the following args as text
* - c read the content of elements matching the selector
* /
//#content
export async function ttsread ( mode : "-t" | "-c" , . . . args : string [ ] ) {
if ( mode === "-t" ) {
// really should quote args, but for now, join
TTS . readText ( args . join ( " " ) )
}
else if ( mode === "-c" ) {
if ( args . length > 0 ) {
tssReadFromCss ( args [ 0 ] )
} else {
console . log ( "Error: no CSS selector supplied" )
}
} else {
console . log ( "Unknown mode for ttsread command: " + mode )
}
}
/ * *
* Show a list of the voices available to the TTS system . These can be
* set in the config using ` ttsvoice `
* /
//#background
export async function ttsvoices() {
let voices = TTS . listVoices ( )
// need a better way to show this to the user
fillcmdline_notrail ( voices . sort ( ) . join ( ", " ) )
}
/ * *
* Cancel current reading and clear pending queue
*
* Arguments :
* - stop : cancel current and pending utterances
* /
//#content
export async function ttscontrol ( action : string ) {
let ttsAction : TTS.Action = null
// convert user input to TTS.Action
// only pause seems to be working, so only provide access to that
// to avoid exposing users to things that won't work
switch ( action ) {
case "stop" :
ttsAction = "stop"
break
}
if ( ttsAction ) {
TTS . doAction ( ttsAction )
} else {
console . log ( "Unknown text-to-speech action: " + action )
}
}
//}}}
2017-11-22 11:54:17 +00:00
// unsupported on android
2017-11-22 12:18:41 +00:00
/ * * A d d o r r e m o v e a b o o k m a r k .
*
* Optionally , you may give the bookmark a title . If no URL is given , a bookmark is added for the current page .
*
* If a bookmark already exists for the URL , it is removed .
* /
2017-11-22 11:54:17 +00:00
//#background
export async function bmark ( url? : string , title = "" ) {
url = url === undefined ? ( await activeTab ( ) ) . url : url
let dupbmarks = await browser . bookmarks . search ( { url } )
dupbmarks . map ( ( bookmark ) = > browser . bookmarks . remove ( bookmark . id ) )
if ( dupbmarks . length == 0 ) { browser . bookmarks . create ( { url , title } ) }
}
2017-11-05 14:10:11 +00:00
2017-10-21 12:54:15 +02:00
// vim: tabstop=4 shiftwidth=4 expandtab