├── COPYING ├── README ├── _toolbar ├── 00-read.js ├── 05-prev.js ├── 10-next.js ├── 15-ffwd.js ├── 20-untrack.js ├── 25-nocookie.js ├── 90-rmi.js └── README ├── bookmarks-without-parameters.html ├── bookmarks.html ├── content ├── README ├── alt2title.js ├── comparecells.js ├── ffwd.js ├── freeviddy.js ├── fullimg.js ├── jump.js ├── nocookie.js ├── read.js ├── rmi.js ├── rmo.js ├── sort.js └── vidspeed.js ├── development ├── README ├── addcss.js ├── b64.js ├── dumphtml.js ├── html.js ├── len.js ├── magic.js ├── reconsole.js ├── reloadcss.js ├── text.js ├── title.js ├── urldec.js └── urlenc.js ├── forms ├── README ├── checkpass.js ├── freeform.js ├── frmblank.js ├── frmcomp.js ├── frmfill.js ├── frmget.js ├── frmpost.js └── showpass.js ├── generate-bookmarks-html ├── language ├── README ├── dictionaries │ ├── README │ ├── ac.js │ ├── dewikt.js │ ├── dict.js │ ├── enwikt.js │ ├── eswikt.js │ ├── frwikt.js │ ├── itwikt.js │ ├── mw.js │ ├── nlwikt.js │ ├── nowikt.js │ ├── oed.js │ ├── ptwikt.js │ ├── ruwikt.js │ ├── svwikt.js │ ├── thes.js │ ├── ud.js │ ├── vd.js │ ├── viwikt.js │ ├── vw.js │ └── zhwikt.js ├── shuo.js └── translations │ ├── 2de.js │ ├── 2en.js │ ├── 2es.js │ ├── 2fr.js │ ├── 2it.js │ ├── 2nl.js │ ├── 2no.js │ ├── 2pt.js │ ├── 2ru.js │ ├── 2sv.js │ ├── 2vi.js │ ├── 2zh.js │ └── README ├── navigation ├── README ├── back.js ├── link.js ├── linksto.js ├── linktitles.js ├── next.js ├── prev.js ├── refer.js ├── relink.js ├── rmh.js ├── rmhq.js ├── rmq.js ├── rmqh.js ├── root.js ├── unarchive.js ├── untrack.js └── up.js ├── search ├── README ├── bimg.js ├── ddg.js ├── ddimg.js ├── e.js ├── eimg.js ├── g.js ├── gc.js ├── gimg.js ├── go.js ├── gs.js ├── img.js ├── maps │ ├── README │ ├── bmap.js │ ├── drive.js │ ├── fly.js │ ├── map.js │ ├── mapi.js │ ├── ride.js │ ├── transit.js │ └── walk.js ├── search.js ├── tineye.js ├── tw.js ├── tweep.js ├── wb.js ├── wikipedia │ ├── 2comm.js │ ├── README │ ├── anyw.js │ ├── dew.js │ ├── enw.js │ ├── esw.js │ ├── frw.js │ ├── itw.js │ ├── nlw.js │ ├── now.js │ ├── ptw.js │ ├── ruw.js │ ├── svw.js │ ├── viw.js │ ├── wikiv.js │ └── zhw.js ├── yanimg.js ├── yt.js └── yttop.js ├── site-specific ├── README ├── discogs.js ├── emo.js ├── githublet.js ├── kbo.js ├── printriodos.js ├── redtop.js ├── somanp.js └── strava │ ├── README │ ├── pace2speed.js │ ├── sact.js │ ├── sath.js │ ├── sclub.js │ ├── shide.js │ ├── sseg.js │ └── sstats.js ├── snippets.bash └── tests ├── README └── content └── sort └── tables.html /COPYING: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2011–2021 Jan Moesen 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Jan Moesen’s bookmarklets 2 | 3 | A collection of time-saving and life-enhancing (ahem) bookmarklets. 4 | 5 | These bookmarklets are used for various purposes, but they all share the same 6 | goal: make web browsing and development easier and more efficient. 7 | 8 | I like giving my bookmarklets keywords so I can use my address bar as a 9 | command line. (If you speak Dutch, check out this presentation from 2011: 10 | http://kak.be/phl.) 11 | 12 | The bookmarklets are stored here as separate script files. You can copy and 13 | paste them into your bookmarks manager, or import them wholesale using the 14 | generated `bookmarks.html` file. 15 | 16 | Feel free to fork and create pull requests: http://kak.be/bookmarklets 17 | 18 | See COPYING for licence details. Or “license details”, if you are so inclined. 19 | -------------------------------------------------------------------------------- /_toolbar/00-read.js: -------------------------------------------------------------------------------- 1 | ../content/read.js -------------------------------------------------------------------------------- /_toolbar/05-prev.js: -------------------------------------------------------------------------------- 1 | ../navigation/prev.js -------------------------------------------------------------------------------- /_toolbar/10-next.js: -------------------------------------------------------------------------------- 1 | ../navigation/next.js -------------------------------------------------------------------------------- /_toolbar/15-ffwd.js: -------------------------------------------------------------------------------- 1 | ../content/ffwd.js -------------------------------------------------------------------------------- /_toolbar/20-untrack.js: -------------------------------------------------------------------------------- 1 | ../navigation/untrack.js -------------------------------------------------------------------------------- /_toolbar/25-nocookie.js: -------------------------------------------------------------------------------- 1 | ../content/nocookie.js -------------------------------------------------------------------------------- /_toolbar/90-rmi.js: -------------------------------------------------------------------------------- 1 | ../content/rmi.js -------------------------------------------------------------------------------- /_toolbar/README: -------------------------------------------------------------------------------- 1 | Bookmarks toolbar 2 | 3 | These are the bookmarklets I keep on the bookmarklets toolbar for quick access 4 | with the mouse. For instance, when browsing a web comic, it is easier to keep 5 | the cursor positioned over the "Next »" and just keep clicking it, rather than 6 | typing "next" every time. (Or looking for the "Next" link on the web 7 | page itself.) 8 | -------------------------------------------------------------------------------- /content/README: -------------------------------------------------------------------------------- 1 | Content 2 | 3 | Tweak the page's content. 4 | -------------------------------------------------------------------------------- /content/alt2title.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Set the IMG@title to the IMG@alt attribute. If the image already has a title 3 | * attribute, separate the title and alt text with a blank line. 4 | * 5 | * @title Alt to title 6 | */ 7 | (function alt2title() { 8 | var processedDocuments = []; 9 | 10 | (function execute(document) { 11 | /* Recurse for (i)frames. */ 12 | try { 13 | Array.from(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach( 14 | elem => { try { execute(elem.contentDocument) } catch (e) { } } 15 | ); 16 | } catch (e) { 17 | /* Catch and ignore exceptions for out-of-domain access. */ 18 | } 19 | 20 | if (processedDocuments.indexOf(document) > -1) { 21 | return; 22 | } 23 | 24 | processedDocuments.push(document); 25 | 26 | function setTitleForElement(element) { 27 | if (!element || element.hasBeenProcessedByAlt2Title) 28 | { 29 | return; 30 | } 31 | 32 | var newTitle = ''; 33 | if (element.hasAttribute('title')) { 34 | newTitle = element.getAttribute('title') + '\n\n'; 35 | } 36 | 37 | if (!element.hasAttribute('alt')) { 38 | newTitle += '(Missing alt text)'; 39 | } else { 40 | var alt = element.getAttribute('alt'); 41 | 42 | if (alt === '') { 43 | newTitle += '(Empty alt text)'; 44 | } else { 45 | newTitle += 'Alt text: “' + alt + '”'; 46 | } 47 | } 48 | 49 | element.setAttribute('title', newTitle); 50 | 51 | console.log('Alt to title: setting title for ', element, ' to: ', newTitle); 52 | 53 | element.hasBeenProcessedByAlt2Title = true; 54 | }; 55 | 56 | /* Process all existing images. */ 57 | var imageSelector = 'img, input[type="image"], area'; 58 | [].forEach.call(document.querySelectorAll(imageSelector), setTitleForElement); 59 | 60 | /* Add a hover handler for images that may be added in the future. */ 61 | document.addEventListener('mousemove', function (e) { 62 | var element; 63 | if (e.target && typeof e.target.closest === 'function' && (element = e.target.closest(imageSelector))) { 64 | setTitleForElement(element); 65 | } 66 | }, false); 67 | })(document); 68 | })(); 69 | -------------------------------------------------------------------------------- /content/ffwd.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Speed up the video and audio playback rate, starting from 10x back to 1x by 3 | * executing this bookmarklet multiple times. Executing it once more after the 4 | * normal speed will restart the cycle from the maximum speed again. 5 | * 6 | * @title FFWD ⏩ 7 | */ 8 | (function ffwd() { 9 | 'use strict'; 10 | 11 | const playbackRates = [10, 4, 2, 1.5, 1]; 12 | 13 | let playbackRateToUse = undefined; 14 | 15 | /* Recursively execute the logic on the document and its sub-documents. */ 16 | function execute(document) { 17 | let allMedia = Array.from(document.querySelectorAll('video, audio')); 18 | 19 | /* Find video and audio inside the shadow DOM of custom elements (Web Components). */ 20 | const notRegularHtmlElementsSelector = 'a,abbr,address,area,article,aside,audio,b,base,bdi,bdo,blockquote,body,br,button,canvas,caption,cite,code,col,colgroup,data,datalist,dd,del,details,dfn,dialog,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,img,input,ins,kbd,label,legend,li,link,main,map,mark,math,math *,menu,meta,meter,nav,noscript,object,ol,optgroup,option,output,p,param,picture,pre,progress,q,rp,rt,ruby,s,samp,script,section,select,slot,small,source,span,strong,style,sub,summary,sup,svg,svg *,table,tbody,td,template,textarea,tfoot,th,thead,time,title,tr,track,u,ul,var,video,wbr' 21 | .split(',') 22 | .map(s => `:not(${s})`) 23 | .join(''); 24 | 25 | Array.from(document.querySelectorAll(notRegularHtmlElementsSelector)) 26 | .filter(elem => elem.shadowRoot) 27 | .forEach(elem => allMedia = allMedia.concat(Array.from(elem.shadowRoot.querySelectorAll('video, audio')))); 28 | 29 | allMedia.forEach(media => { 30 | /* Determine the playback rate to use on all video/audio, based 31 | * on the first element encountered. */ 32 | if (typeof playbackRateToUse === 'undefined') { 33 | for (let i = 0; i < playbackRates.length; i++) { 34 | if (media.playbackRate >= playbackRates[i]) { 35 | playbackRateToUse = i === playbackRates.length - 1 36 | ? playbackRates[0] 37 | : playbackRates[i + 1]; 38 | break; 39 | } 40 | } 41 | } 42 | 43 | media.playbackRate = playbackRateToUse; 44 | 45 | /* Indicate the new playback rate (speed) for the media element. */ 46 | let visibleMediaContainer = media; 47 | let rect = visibleMediaContainer.getBoundingClientRect(); 48 | while ( 49 | (rect.height <= 0 || rect.width <= 0) 50 | && (visibleMediaContainer = visibleMediaContainer.parentNode) 51 | && (typeof visibleMediaContainer.getBoundingClientRect === 'function') 52 | ) { 53 | rect = visibleMediaContainer.getBoundingClientRect(); 54 | } 55 | 56 | const indicator = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); 57 | indicator.textContent = media.playbackRate + '×'; 58 | indicator.setAttribute('style', ` 59 | display: flex; 60 | position: fixed; 61 | left: ${Math.max(0, rect.left)}px; 62 | top: ${Math.max(0, rect.top)}px; 63 | z-index: 10000; 64 | width: ${rect.width}px; 65 | height: ${rect.height}px; 66 | font-size: ${rect.height / 10}px; 67 | justify-content: center; 68 | align-items: center; 69 | color: rgba(0, 0, 0, 1); 70 | text-shadow: 0 0 10px rgba(255, 255, 255, 1); 71 | transition: all 0.75s ease-out; 72 | pointer-events: none; 73 | `.replace(/^\s*|\s*$/gm, '')); 74 | 75 | document.body.appendChild(indicator); 76 | 77 | /* Animate and remove the indicator. */ 78 | setTimeout(_ => { 79 | setTimeout( _ => indicator.remove(), 750); 80 | 81 | indicator.setAttribute('style', indicator.getAttribute('style') + ` 82 | font-size: ${rect.height}px; 83 | color: rgba(0, 0, 0, 0); 84 | text-shadow: 0 0 10px rgba(255, 255, 255, 0); 85 | `.replace(/^\s*|\s*$/gm, '')); 86 | }, 0); 87 | }); 88 | 89 | /* Recurse for (i)frames. */ 90 | try { 91 | Array.from(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach( 92 | elem => { try { execute(elem.contentDocument) } catch (e) { } } 93 | ); 94 | } catch (e) { 95 | /* Catch and ignore exceptions for out-of-domain access. */ 96 | } 97 | } 98 | 99 | execute(document); 100 | })(); 101 | -------------------------------------------------------------------------------- /content/freeviddy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Free the VIDEO elements: get rid of overlays, and enable the native controls. 3 | * 4 | * Useful on https://www.instagram.com/ where the stupid overlays prevent 5 | * showing the controls and triggering the context menu, so you don’t know how 6 | * long the video will take and can't play the video in full screen mode. 7 | * 8 | * @title Free Viddy 9 | */ 10 | (function freeviddy() { 11 | "use strict"; 12 | 13 | /* Recursively execute the main logic on the document and its sub-documents. */ 14 | function execute(document) { 15 | document.addEventListener('mousemove', function debouncer(event) { 16 | clearTimeout(debouncer.timeoutId); 17 | debouncer.timeoutId = setTimeout(function () { 18 | let elementsUnderPointer = document.elementsFromPoint(event.clientX, event.clientY); 19 | let overlaysToRemove = []; 20 | 21 | for (let i = 0; i < elementsUnderPointer.length; i++) { 22 | if (elementsUnderPointer[i].tagName.toUpperCase() === 'VIDEO' && !elementsUnderPointer[i].xxxJanFreeViddyProcessed) { 23 | let video = elementsUnderPointer[i]; 24 | video.controls = true; 25 | video.xxxJanFreeViddyProcessed = true; 26 | 27 | if (i === 0) { 28 | console.log('Free Viddy: found video without overlays:', video); 29 | } else { 30 | overlaysToRemove = elementsUnderPointer.slice(0, i); 31 | console.log(`Free Viddy: found video with ${i} overlays:`, video); 32 | } 33 | 34 | break; 35 | } 36 | } 37 | 38 | if (overlaysToRemove.length) { 39 | overlaysToRemove.forEach(element => element.remove()); 40 | } 41 | }, 50); 42 | }); 43 | 44 | /* Recurse for (i)frames. */ 45 | try { 46 | Array.from(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach( 47 | elem => { try { execute(elem.contentDocument) } catch (e) { } } 48 | ); 49 | } catch (e) { 50 | /* Catch and ignore exceptions for out-of-domain access. */ 51 | } 52 | } 53 | 54 | execute(document); 55 | })(); 56 | -------------------------------------------------------------------------------- /content/rmi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Remove all the IFRAMEs that are off-site or do not have a “src” attribute. 3 | * These are typically used for ads and unwanted external content. 4 | * navigation etc. 5 | * 6 | * IFRAMEs without a “src” attribute are also used for sandboxing untrusted 7 | * content, e.g. on mailinator.com, but I have not yet found a way to 8 | * distinguish between src-less IFRAMEs for ads and src-less IFRAMEs for 9 | * “regular” content. Maybe try to guess based on the dimensions? Meh. 10 | * 11 | * @title rm IFRAMEs 12 | */ 13 | (function rmi() { 14 | /* Keep track of the HTMLDocument instances we have processed. */ 15 | let processed = new Set(); 16 | 17 | /** 18 | * Is the given node empty-ish? I.e., does it lack child elements and 19 | * non-whitespace text? 20 | */ 21 | function isEmpty(node) { 22 | return !node || (!node.childElementCount && (typeof node.textContent !== 'string' || node.textContent.trim() === '')); 23 | } 24 | 25 | /* The main function. */ 26 | (function execute(document) { 27 | if (!document || typeof document.querySelectorAll !== 'function' || processed.has(document)) { 28 | return; 29 | } 30 | 31 | processed.add(document); 32 | 33 | /* Process all IFRAMEs. */ 34 | Array.from(document.querySelectorAll('iframe:not(#xxxJanConsole)')).forEach(iframe => { 35 | let shouldDelete = false; 36 | try { 37 | shouldDelete = iframe.contentDocument === null || iframe.src === ''; 38 | } catch (e) { 39 | shouldDelete = true; 40 | } 41 | 42 | if (shouldDelete) { 43 | console.log('rm IFRAMEs: found suspicious IFRAME to delete: ', iframe); 44 | let parentNode = iframe.parentNode; 45 | iframe.remove(); 46 | 47 | while (parentNode && isEmpty(parentNode)) { 48 | console.log('rm IFRAMEs: found empty parent node to delete: ', parentNode); 49 | let oldParentNode = parentNode; 50 | parentNode = parentNode.parentNode; 51 | oldParentNode.remove(); 52 | } 53 | } else { 54 | console.log('rm IFRAMEs: found non-suspicious IFRAME to recurse into: ', iframe); 55 | execute(iframe.contentDocument); 56 | } 57 | }); 58 | })(document); 59 | })(); 60 | -------------------------------------------------------------------------------- /content/vidspeed.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Change the video and audio speed (playback rate). 3 | * 4 | * @title Set video speed 5 | */ 6 | (function vidspeed() { 7 | "use strict"; 8 | 9 | /* Recursively get all video and audio for the document and its sub-documents. */ 10 | function getMedia(document) { 11 | let allMedia = Array.from(document.querySelectorAll('video, audio')); 12 | 13 | /* Find video and audio inside the shadow DOM of custom elements (Web Components). */ 14 | const notRegularHtmlElementsSelector = 'a,abbr,address,area,article,aside,audio,b,base,bdi,bdo,blockquote,body,br,button,canvas,caption,cite,code,col,colgroup,data,datalist,dd,del,details,dfn,dialog,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,img,input,ins,kbd,label,legend,li,link,main,map,mark,math,math *,menu,meta,meter,nav,noscript,object,ol,optgroup,option,output,p,param,picture,pre,progress,q,rp,rt,ruby,s,samp,script,section,select,slot,small,source,span,strong,style,sub,summary,sup,svg,svg *,table,tbody,td,template,textarea,tfoot,th,thead,time,title,tr,track,u,ul,var,video,wbr' 15 | .split(',') 16 | .map(s => `:not(${s})`) 17 | .join(''); 18 | 19 | Array.from(document.querySelectorAll(notRegularHtmlElementsSelector)) 20 | .filter(elem => elem.shadowRoot) 21 | .forEach(elem => allMedia = allMedia.concat(Array.from(elem.shadowRoot.querySelectorAll('video, audio')))); 22 | 23 | /* Recurse for frames and iframes. */ 24 | try { 25 | Array.from( 26 | document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]') 27 | ).forEach( 28 | elem => allMedia = allMedia.concat(getMedia(elem.contentDocument)) 29 | ); 30 | } catch (e) { 31 | /* Catch exceptions for out-of-domain access, but do not do anything with them. */ 32 | } 33 | 34 | return allMedia; 35 | } 36 | 37 | let allMedia = getMedia(document); 38 | 39 | /* Make sure there are media elements. */ 40 | if (!allMedia.length) { 41 | return; 42 | } 43 | 44 | /* Try to get the parameter string from the bookmarklet/search query. 45 | * If there is no parameter, prompt the user. */ 46 | let s = (function () { /*%s*/; }).toString() 47 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 48 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 49 | .replace(/\u0025s/, ''); 50 | 51 | if (s === '') { 52 | s = prompt('Specify the speed (playback rate) as a number, with 1 being 100%. For example, 1.25 = 125%, 0.75 = 75%, and 1 = 100%.', allMedia[0].playbackRate); 53 | } 54 | 55 | if (s) { 56 | allMedia.forEach(media => { 57 | media.playbackRate = s; 58 | 59 | /* Indicate the new playback rate (speed) for the media element. */ 60 | let visibleMediaContainer = media; 61 | let rect = visibleMediaContainer.getBoundingClientRect(); 62 | while ( 63 | (rect.height <= 0 || rect.width <= 0) 64 | && (visibleMediaContainer = visibleMediaContainer.parentNode) 65 | && (typeof visibleMediaContainer.getBoundingClientRect === 'function') 66 | ) { 67 | rect = visibleMediaContainer.getBoundingClientRect(); 68 | } 69 | 70 | const indicator = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); 71 | indicator.textContent = media.playbackRate + '×'; 72 | indicator.setAttribute('style', ` 73 | display: flex; 74 | position: fixed; 75 | left: ${Math.max(0, rect.left)}px; 76 | top: ${Math.max(0, rect.top)}px; 77 | z-index: 10000; 78 | width: ${rect.width}px; 79 | height: ${rect.height}px; 80 | font-size: ${rect.height / 10}px; 81 | justify-content: center; 82 | align-items: center; 83 | color: rgba(0, 0, 0, 1); 84 | text-shadow: 0 0 10px rgba(255, 255, 255, 1); 85 | transition: all 0.75s ease-out; 86 | pointer-events: none; 87 | `.replace(/^\s*|\s*$/gm, '')); 88 | 89 | document.body.appendChild(indicator); 90 | 91 | /* Animate and remove the indicator. */ 92 | setTimeout(_ => { 93 | setTimeout( _ => indicator.remove(), 750); 94 | 95 | indicator.setAttribute('style', indicator.getAttribute('style') + ` 96 | font-size: ${rect.height}px; 97 | color: rgba(0, 0, 0, 0); 98 | text-shadow: 0 0 10px rgba(255, 255, 255, 0); 99 | `.replace(/^\s*|\s*$/gm, '')); 100 | }, 0); 101 | }); 102 | } 103 | })(); 104 | -------------------------------------------------------------------------------- /development/README: -------------------------------------------------------------------------------- 1 | Development 2 | 3 | Tools to help development. 4 | -------------------------------------------------------------------------------- /development/addcss.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Add the specified CSS to the current document. 3 | * 4 | * @title Add CSS 5 | */ 6 | (function addcss(document, s) { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | s = s || (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter your CSS code:'); 62 | } else if (s.includes('~') && !s.includes('{')) { 63 | /* Only replace `~` with the active selection if the entire parameter 64 | * string does not look like a CSS rule. For this bookmarklet, `~` is 65 | * more likely to refer to the sibling combinator, e.g. `p ~ ul` than 66 | * to the “replace this with the selection” magic like in the other 67 | * bookmarklets. */ 68 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 69 | } 70 | 71 | if (s) { 72 | (document.head || document.body || document.documentElement).appendChild(document.createElementNS('http://www.w3.org/1999/xhtml', 'style')).textContent = s; 73 | 74 | /* Recurse for frames and iframes. */ 75 | try { 76 | Array.prototype.slice.call(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach(function (elem) { 77 | addcss(elem.contentDocument, s); 78 | }); 79 | } catch (e) { 80 | /* Catch exceptions for out-of-domain access, but do not do anything with them. */ 81 | } 82 | } 83 | })(document); 84 | -------------------------------------------------------------------------------- /development/b64.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Convert plain text to Base64 and back. It determines which conversion to do. 3 | * 4 | * @title Base64 5 | */ 6 | (function b64() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the text to encode/decode using Base64:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | var result, operation; 68 | 69 | /* The try/catch block wraps the atob() call and the fake exception to force btoa(). */ 70 | try { 71 | /* A string "looks like Base64" if it: 72 | * - uses only the Base64 alphabet [A-Za-z0-9+/] (with optional trailing "=") 73 | * - has at least one uppercase character followed by a lowercase character 74 | * (or the other way around) other than at the beginning of the string. 75 | * This way, strings like "test" or "Hello", which are valid Base64 strings 76 | * will not be decoded, since they most likely are not really Base64 encoded. 77 | */ 78 | if (!s.trim().match(/^[A-Za-z0-9+/]+([A-Z][a-z]|[a-z][A-Z])[A-Za-z0-9+/]*={0,2}$/)) { 79 | throw 'I guess this should be encoded, rather than encoded.'; 80 | } 81 | s = s.trim(); 82 | result = atob(s); 83 | operation = 'decoded'; 84 | } 85 | catch (e) { 86 | result = btoa(s); 87 | operation = 'encoded'; 88 | } 89 | 90 | var text = 'The Base64 ' + operation + ' string of "' + s + '" is:\n\n' + result; 91 | 92 | /* Replace the original document's HTML with our result. */ 93 | HTMLDocument.prototype.open.call(document, 'text/plain; charset=UTF-8'); 94 | HTMLDocument.prototype.write.call(document, text); 95 | HTMLDocument.prototype.close.call(document); 96 | } 97 | })(); 98 | -------------------------------------------------------------------------------- /development/html.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Render the specified or selected text as HTML. 3 | * 4 | * @title View as HTML 5 | */ 6 | (function html() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter your HTML snippet:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | /* Replace the original document's HTML with the desired HTML. */ 68 | HTMLDocument.prototype.open.call(document, 'text/html; charset=UTF-8'); 69 | HTMLDocument.prototype.write.call(document, s); 70 | HTMLDocument.prototype.close.call(document); 71 | } 72 | })(); 73 | -------------------------------------------------------------------------------- /development/len.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Show the length of the given string. 3 | * 4 | * @title Show length 5 | */ 6 | (function len() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter your string to measure:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | /* To count the number of characters, we cannot rely on String.length 68 | * because of astral chars. Use this code from BestieJS to deal with 69 | * those: 70 | * https://github.com/bestiejs/punycode.js/blob/8164242ef1/punycode.js#L99-126 71 | */ 72 | var codePoints = [], counter = 0, length = s.length, value, extra; 73 | while (counter < length) { 74 | value = s.charCodeAt(counter++); 75 | if ((value & 0xF800) === 0xD800) { 76 | extra = s.charCodeAt(counter++); 77 | if ((value & 0xFC00) != 0xD800 || (extra & 0xFC00) != 0xDC00) { 78 | alert('Illegal UTF-16 sequence, but continuing anyway.'); 79 | } 80 | value = ((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000; 81 | } 82 | codePoints.push(value); 83 | } 84 | var numChars = codePoints.length; 85 | 86 | /* To count the number of bytes, we URL-encode the string so the 87 | * non-ASCII characters are encoded as sequences of %XX. We then 88 | * replace those triplets by a single character, "x", and count the 89 | * number of characters in the resulting string. 90 | */ 91 | var numBytes = encodeURI(s).replace(/%../g, 'x').length; 92 | 93 | /* Shorten the string before displaying, if necessary. */ 94 | var maxDisplayLength = 64; 95 | if (numChars > maxDisplayLength) { 96 | var encodeUtf16 = function (value) { 97 | var output = ''; 98 | if ((value & 0xF800) == 0xD800) { 99 | alert('Invalid UTF-16 value, but continuing anyway.'); 100 | } 101 | if (value > 0xFFFF) { 102 | value -= 0x10000; 103 | output += String.fromCharCode(value >>> 10 & 0x3FF | 0xD800); 104 | value = 0xDC00 | value & 0x3FF; 105 | } 106 | output += String.fromCharCode(value); 107 | return output; 108 | }; 109 | s = codePoints.slice(0, maxDisplayLength / 2).map(encodeUtf16).join('') 110 | + '…' 111 | + codePoints.slice(numChars - maxDisplayLength / 2 + 1).map(encodeUtf16).join(''); 112 | } 113 | 114 | var displayString = numChars === numBytes 115 | ? 'The number of characters in the ASCII string "' + s + '" is: ' 116 | : 'The number of characters in the non-ASCII string "' + s + '" (' + numBytes + ' UTF-8 bytes) is: '; 117 | prompt(displayString, numChars); 118 | } 119 | })(); 120 | -------------------------------------------------------------------------------- /development/magic.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use GCHQ’s CyberChef to magically determine what the input data is, e.g. 3 | * a Base64-encoded string of gzipped plaintext, a JSON Web Token, … 4 | * 5 | * By default, this bookmarklet tells CyberChef to only go two levels deep to 6 | * save time and energy. 7 | * 8 | * E.g. `magic H4sIAL8d32UAAyvJSFXIL8pMz8xLzFEoyEnMzCtJrSjRAwByt2jzFwAAAA` 9 | * will use CyberChef to produce this recipe: 10 | * 1) `From_Base64('A-Za-z0-9+/=',true,false)` 11 | * 2) `Gunzip()` 12 | * which leads to: `the original plaintext.` 13 | * 14 | * @title CyberChef Magic 15 | */ 16 | (function magic(document) { 17 | 'use strict'; 18 | 19 | /* Try to get the parameter string from the bookmarklet/search query. 20 | * Fall back to the current text selection, if any. If those options 21 | * both fail, prompt the user. */ 22 | let s = (function () { /*%s*/; }).toString() 23 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 24 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 25 | .replace(/\u0025s/, ''); 26 | 27 | /** 28 | * Get the active text selection, diving into frames and 29 | * text controls when necessary and possible. 30 | */ 31 | function getActiveSelection(document) { 32 | if (!document || typeof document.getSelection !== 'function') { 33 | return ''; 34 | } 35 | 36 | if (!document.activeElement) { 37 | return document.getSelection() + ''; 38 | } 39 | 40 | const activeElement = document.activeElement; 41 | 42 | /* Recurse for FRAMEs and IFRAMEs. */ 43 | try { 44 | if ( 45 | typeof activeElement.contentDocument === 'object' 46 | && activeElement.contentDocument !== null 47 | ) { 48 | return getActiveSelection(activeElement.contentDocument); 49 | } 50 | } catch (e) { 51 | return document.getSelection() + ''; 52 | } 53 | 54 | /* Get the selection from inside a text control. */ 55 | if (typeof activeElement.value === 'string') { 56 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 57 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 58 | } 59 | 60 | return activeElement.value; 61 | } 62 | 63 | /* Get the normal selection. */ 64 | return document.getSelection() + ''; 65 | } 66 | 67 | if (s === '') { 68 | s = getActiveSelection(document) || prompt('Please enter your input data for CyberChef’s magic mode:'); 69 | } else { 70 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection(document) + '$2'); 71 | } 72 | 73 | if (s) { 74 | location = 'https://gchq.github.io/CyberChef/#recipe=Magic(2,false,false,%27%27)&input=' + encodeURIComponent(btoa(s).replace(/=*$/, '')); 75 | } 76 | })(document); 77 | -------------------------------------------------------------------------------- /development/reconsole.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Restore window.console to its native state. 3 | * 4 | * @title Re-console 5 | */ 6 | (function reconsole() { 7 | 'use strict'; 8 | 9 | /* Sometimes all it takes to revert back to the original `console`, is to 10 | * delete the imposter. */ 11 | delete window.console; 12 | 13 | /* But if that didn’t work, create an IFRAME and use its `console`. */ 14 | if (!window.console) { 15 | const iframe = document.body.appendChild(document.createElement('iframe')); 16 | window.console = iframe.contentWindow.console; 17 | } 18 | })(); 19 | -------------------------------------------------------------------------------- /development/reloadcss.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Reload all external style sheets. 3 | * 4 | * @title Reload CSS 5 | */ 6 | (function reloadcss() { 7 | /** 8 | * Reload the given style sheet by creating a new LINK element with the updated URL. 9 | * 10 | * @param mixed item Either a StyleSheet or a CSSRule; something with "href" and "media.mediaText". 11 | */ 12 | function update(item) { 13 | if (item.disabled) { 14 | return; 15 | } 16 | 17 | var timestamp = +new Date() + ''; 18 | var paramName = 'janbm-date', paramRegex = new RegExp('([?&])' + paramName + '=[0-9]{' + timestamp.length + '}\\b'); 19 | 20 | /* Replace the previous timestamp query string parameter, if present. If not, add it. */ 21 | var newHref = item.href.replace(paramRegex, ''); 22 | if(!newHref.match(paramRegex)) { 23 | newHref += ~newHref.indexOf('?') 24 | ? '&' + paramName + '=' + timestamp 25 | : '?' + paramName + '=' + timestamp; 26 | } 27 | 28 | /* Get the full @media rule for the nested style. */ 29 | var tmpItem = item; 30 | var allMedia = []; 31 | while (tmpItem) { 32 | tmpItem.media.mediaText && allMedia.unshift(tmpItem.media.mediaText); 33 | tmpItem = tmpItem.parentStyleSheet; 34 | } 35 | 36 | /* Reload the style sheet by creating a new LINK element with the updated URL. */ 37 | var newStyleSheet = document.createElementNS('http://www.w3.org/1999/xhtml', 'link'); 38 | newStyleSheet.rel = 'StyleSheet'; 39 | newStyleSheet.media = allMedia.join(', '); 40 | newStyleSheet.href = newHref; 41 | (document.head || document.body || document.documentElement).appendChild(newStyleSheet); 42 | item.disabled = true; 43 | } 44 | 45 | Array.prototype.slice.call(document.styleSheets).forEach(function (styleSheet, i) { 46 | if (!styleSheet.href) { 47 | /* Inline style sheets can still refer to external style sheets using @import rules. */ 48 | Array.prototype.slice.call(styleSheet.cssRules).forEach(function (cssRule) { 49 | if (cssRule.type === cssRule.IMPORT_RULE) { 50 | update(cssRule); 51 | } 52 | }); 53 | return; 54 | } 55 | 56 | update(styleSheet); 57 | }); 58 | })(); 59 | -------------------------------------------------------------------------------- /development/text.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Render the specified or selected text as plain text. 3 | * 4 | * @title View as text 5 | */ 6 | (function text() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the text to show:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | /* Replace the original document's HTML with our text. */ 68 | HTMLDocument.prototype.open.call(document, 'text/html; charset=UTF-8'); 69 | HTMLDocument.prototype.write.call(document, '' + s); 70 | HTMLDocument.prototype.close.call(document); 71 | } 72 | })(); 73 | -------------------------------------------------------------------------------- /development/title.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Set the document title to the specified text. 3 | * 4 | * @title Set title 5 | */ 6 | (function title() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the title:', document.title); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s !== null) { 67 | document.title = s; 68 | 69 | /* Also update the og:title META tags. (Used by the “link” bookmarklet.) */ 70 | Array.from(document.querySelectorAll('meta[property="og:title"], meta[property="twitter:title"]')).forEach( 71 | title => title.setAttribute('content', s) 72 | ); 73 | } 74 | })(); 75 | -------------------------------------------------------------------------------- /development/urldec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Decode all %XX bytes into a (hopefully) readable string. 3 | * 4 | * @title URL-decode 5 | */ 6 | (function urldec() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the text to URL-decode:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | /* Replace the original document's HTML with our generated text. */ 68 | HTMLDocument.prototype.open.call(document, 'text/plain; charset=UTF-8'); 69 | HTMLDocument.prototype.write.call(document, decodeURIComponent(s)); 70 | HTMLDocument.prototype.close.call(document); 71 | } 72 | })(); 73 | -------------------------------------------------------------------------------- /development/urlenc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Change every character to its percent-encoded form (%XX), even if they are 3 | * URL-safe. (This makes "URL-encode" a bit of a misnomer.) 4 | * 5 | * I use this mainly for pranks. If I want to reply with a link to Google 6 | * Images, I will typically obfuscate the search term so as not to give it 7 | * away immediately. For example: 8 | * https://www.google.com/images?q=cool+story+bro 9 | * https://www.google.com/images?q=%63%6F%6F%6C%20%73%74%6F%72%79%20%62%72%6F 10 | * 11 | * @title URL-encode 12 | */ 13 | (function urlenc() { 14 | /* Try to get the parameter string from the bookmarklet/search query. 15 | Fall back to the current text selection, if any. If those options 16 | both fail, prompt the user. 17 | */ 18 | var s = (function () { /*%s*/; }).toString() 19 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 20 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 21 | .replace(/\u0025s/, ''); 22 | 23 | /** 24 | * Get the active text selection, diving into frames and 25 | * text controls when necessary and possible. 26 | */ 27 | function getActiveSelection(doc) { 28 | if (arguments.length === 0) { 29 | doc = document; 30 | } 31 | 32 | if (!doc || typeof doc.getSelection !== 'function') { 33 | return ''; 34 | } 35 | 36 | if (!doc.activeElement) { 37 | return doc.getSelection() + ''; 38 | } 39 | 40 | var activeElement = doc.activeElement; 41 | 42 | /* Recurse for FRAMEs and IFRAMEs. */ 43 | try { 44 | if ( 45 | typeof activeElement.contentDocument === 'object' 46 | && activeElement.contentDocument !== null 47 | ) { 48 | return getActiveSelection(activeElement.contentDocument); 49 | } 50 | } catch (e) { 51 | return doc.getSelection() + ''; 52 | } 53 | 54 | /* Get the selection from inside a text control. */ 55 | if (typeof activeElement.value === 'string') { 56 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 57 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 58 | } 59 | 60 | return activeElement.value; 61 | } 62 | 63 | /* Get the normal selection. */ 64 | return doc.getSelection() + ''; 65 | } 66 | 67 | if (s === '') { 68 | s = getActiveSelection() || prompt('Please enter the text to URL-encode:'); 69 | } else { 70 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 71 | } 72 | 73 | if (s) { 74 | s = s.split('').map(function (c) { 75 | return '%' + ('0' + c.charCodeAt(0).toString(16)).slice(-2); 76 | }).join('').toUpperCase(); 77 | 78 | /* Replace the original document's HTML with our generated text. */ 79 | HTMLDocument.prototype.open.call(document, 'text/plain; charset=UTF-8'); 80 | HTMLDocument.prototype.write.call(document, s); 81 | HTMLDocument.prototype.close.call(document); 82 | } 83 | })(); 84 | -------------------------------------------------------------------------------- /forms/README: -------------------------------------------------------------------------------- 1 | Forms 2 | 3 | Manipulate HTML forms. 4 | -------------------------------------------------------------------------------- /forms/checkpass.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Check whether password and probable password confirmation INPUTs have the 3 | * same value. 4 | * 5 | * @title Check passwords 6 | */ 7 | (function checkpass() { 8 | "use strict"; 9 | 10 | function execute(document) { 11 | let needsStyleSheet = false; 12 | 13 | Array.from(document.querySelectorAll('form input[type="password"], form input[autocomplete="new-password"]')).forEach(input => { 14 | if (input.form.xxxJanHasCheckpass) { 15 | return; 16 | } 17 | 18 | /* Make sure there are at least two password INPUTs. If there 19 | * are two inputs (regardless of their INPUT@type) having the 20 | * INPUT@autocomplete attribute set to `new-password`, we use 21 | * those two. 22 | * 23 | * Otherwise, look for inputs whose INPUT@type is `password`. 24 | * If there are more than two, assume only the last two to be 25 | * “linked”. For instance, on a form to change your password, 26 | * there are typically three password INPUTs: your current 27 | * password, the new password, and a confirmation for the new 28 | * password. On a registration form, there are typically just 29 | * two password INPUTs, one for the password, and one for the 30 | * confirmation. */ 31 | let allPasswordInputs = Array.from(input.form.querySelectorAll('input[autocomplete="new-password"]')); 32 | if (allPasswordInputs.length < 2) { 33 | allPasswordInputs = Array.from(input.form.querySelectorAll('input[type="password"]')); 34 | } 35 | 36 | if (allPasswordInputs.length < 2) { 37 | return; 38 | } 39 | 40 | let firstIndex = allPasswordInputs.length - 2; 41 | let secondIndex = allPasswordInputs.length - 1; 42 | function comparePasswords() { 43 | if (allPasswordInputs[firstIndex].value === allPasswordInputs[secondIndex].value) { 44 | allPasswordInputs[firstIndex].classList.remove('xxxJanPasswordMismatch'); 45 | allPasswordInputs[secondIndex].classList.remove('xxxJanPasswordMismatch'); 46 | } else { 47 | allPasswordInputs[secondIndex].classList.add('xxxJanPasswordMismatch'); 48 | allPasswordInputs[firstIndex].classList.add('xxxJanPasswordMismatch'); 49 | } 50 | } 51 | 52 | allPasswordInputs[firstIndex].addEventListener('input', comparePasswords); 53 | allPasswordInputs[secondIndex].addEventListener('input', comparePasswords); 54 | comparePasswords(); 55 | 56 | input.form.xxxJanHasCheckpass = true; 57 | 58 | needsStyleSheet = true; 59 | }); 60 | 61 | /* Add the style sheet for the (mis)matching password INPUTs. */ 62 | if (needsStyleSheet) { 63 | let styleSheet = document.getElementById('xxxJanCheckpassCss'); 64 | if (!styleSheet) { 65 | styleSheet = document.createElementNS('http://www.w3.org/1999/xhtml', 'style'); 66 | styleSheet.id = 'xxxJanCheckpassCss'; 67 | styleSheet.textContent = ` 68 | .xxxJanPasswordMismatch { 69 | box-shadow: none !important; 70 | background: #f44 !important; 71 | color: #fff !important; 72 | } 73 | `; 74 | document.head.appendChild(styleSheet); 75 | } 76 | } 77 | 78 | /* Recurse for (i)frames. */ 79 | try { 80 | Array.from(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach( 81 | elem => { try { execute(elem.contentDocument) } catch (e) { } } 82 | ); 83 | } catch (e) { 84 | /* Catch and ignore exceptions for out-of-domain access. */ 85 | } 86 | } 87 | 88 | execute(document); 89 | })(); 90 | -------------------------------------------------------------------------------- /forms/frmblank.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Make all forms open in a new window/tab. 3 | * 4 | * @title Form _blank 5 | */ 6 | (function frmblank(document) { 7 | Array.prototype.slice.call(document.forms || document.getElementsByTagName('form')).forEach(function (form) { 8 | form.target = '_blank'; 9 | Array.prototype.slice.call(form.querySelectorAll('input[type="submit"], input[type="image"], button:not([type]), button[type="submit"]')).forEach(function (submit) { 10 | submit.parentNode.insertBefore(document.createTextNode(' [_blank]'), submit.nextSibling); 11 | }); 12 | }); 13 | 14 | /* Recurse for frames and iframes. */ 15 | try { 16 | Array.prototype.slice.call(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach(function (elem) { 17 | frmblank(elem.contentDocument); 18 | }); 19 | } catch (e) { 20 | /* Catch exceptions for out-of-domain access, but do not do anything with them. */ 21 | } 22 | })(document); 23 | -------------------------------------------------------------------------------- /forms/frmcomp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Enable autocompletion on all forms and elements. 3 | * 4 | * @title Enable autocomplete 5 | */ 6 | (function frmcomp(document) { 7 | [].slice.call(document.forms).forEach(function (form) { 8 | if (form.autocomplete === '' || form.autocomplete === 'off') { 9 | console.log('Enable autocomplete: enabling on form: ', form); 10 | form.autocomplete = 'on'; 11 | } 12 | 13 | [].slice.call(form.elements).forEach(function (element) { 14 | if (element.autocomplete === '' || element.autocomplete === 'off') { 15 | console.log('Enable autocomplete: enabling on element: ', element); 16 | element.autocomplete = 'on'; 17 | } 18 | }); 19 | }); 20 | 21 | /* Recurse for frames and iframes. */ 22 | try { 23 | [].slice.call(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach(function (elem) { 24 | autocomp(elem.contentDocument); 25 | }); 26 | } catch (e) { 27 | /* Catch exceptions for out-of-domain access, but do not do anything with them. */ 28 | } 29 | })(document); 30 | -------------------------------------------------------------------------------- /forms/frmfill.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Fill the text inputs with their label contents, their placeholders, titles, 3 | * names or IDs. Fill other, more specialised inputs, with “sensible” defaults. 4 | * 5 | * @title Form fill 6 | */ 7 | (function frmfill() { 8 | /* See if there are options specified as the parameter to the bookmarklet. */ 9 | let s = (function () { /*%s*/; }).toString() 10 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 11 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 12 | .replace(/\u0025s/, ''); 13 | 14 | /* “Be liberal in what you accept”: “--only-fill required only” is fine, too. :-) */ 15 | let shouldOnlyFillRequired = s.match(/^(--)?(only[ -])?(fill[ -])?required([ -]only)?$/); 16 | 17 | function execute(document) { 18 | Array.from(document.querySelectorAll('input, textarea, select')).forEach(input => { 19 | /* If the input already has a value, bail out. */ 20 | if (input.value !== '') { 21 | return; 22 | } 23 | 24 | /* If the input is not required, and we should only fill required inputs, bail out. */ 25 | if (shouldOnlyFillRequired && !(input.required || input.className.match(/required/i))) { 26 | return; 27 | } 28 | 29 | /* “Difficult” input types: do not try to do anything. */ 30 | if (['hidden', 'file', 'password', 'color'].indexOf(input.type) > -1) { 31 | return; 32 | } 33 | 34 | /* Checkboxes and radio buttons: check. */ 35 | if (input.type === 'checkbox' || input.type === 'radio') { 36 | input.checked = true; 37 | return; 38 | } 39 | 40 | /* E-mail inputs: use the host name for a throwaway e-mail address at mailinator.com (using a less common alias). */ 41 | if (input.type === 'email' || [input.name, input.id, input.title, input.placeholder].join(' ').match(/\be-?mail\b/i)) { 42 | input.value = location.hostname.replace(/^www\./, '') + '@veryrealemail.com'; 43 | return; 44 | } 45 | 46 | /* Numeric inputs: use the lowest allowed number (or 1). */ 47 | if (input.type === 'number' || input.type === 'range') { 48 | input.value = input.min === '' 49 | ? 1 50 | : input.min; 51 | return; 52 | } 53 | 54 | /* Date/datetime inputs: use the current date/datetime. */ 55 | if (input.type === 'date') { 56 | input.valueAsDate = new Date(); 57 | return; 58 | } 59 | 60 | if (input.type === 'datetime-local' || input.type === 'datetime') { 61 | input.value = new Date().toISOString().replace(/\..*$/, ''); 62 | return; 63 | } 64 | 65 | /* Time inputs: use the lowest allowed time (or noon). */ 66 | if (input.type === 'time') { 67 | input.value = input.min === '' 68 | ? '12:00' 69 | : input.min; 70 | return; 71 | } 72 | 73 | /* All other inputs: use the label, if any. */ 74 | if (input.labels && input.labels[0]) { 75 | input.value = input.labels[0].textContent 76 | .trim() 77 | /* Get rid of the common “*” required indicator. */ 78 | .replace(/(^\*\s*)|(\s*\*$)/, '') 79 | /* Get rid of the trailing colon in “Label text here:”. */ 80 | .replace(/\s*:$/, ''); 81 | 82 | if (input.value !== '') { 83 | return; 84 | } 85 | } 86 | 87 | /* In case there is no label, fall back to other attributes (in decreasing order of usefulness). */ 88 | var attributes = [ 89 | 'placeholder', 90 | 'title', 91 | 'name', 92 | 'id' 93 | ]; 94 | 95 | for (var i = 0; i < attributes.length; i++) { 96 | var attribute = attributes[i]; 97 | if (input[attribute]) { 98 | input.value = input[attribute]; 99 | return; 100 | } 101 | } 102 | }); 103 | 104 | /* Recurse for (i)frames. */ 105 | try { 106 | Array.from(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach( 107 | elem => { try { execute(elem.contentDocument) } catch (e) { } } 108 | ); 109 | } catch (e) { 110 | /* Catch and ignore exceptions for out-of-domain access. */ 111 | } 112 | } 113 | 114 | execute(document); 115 | })(); 116 | -------------------------------------------------------------------------------- /forms/frmget.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Make all forms use GET. 3 | * 4 | * @title Form GET 5 | */ 6 | (function frmget(document) { 7 | Array.prototype.slice.call(document.forms || document.getElementsByTagName('form')).forEach(function (form) { 8 | form.method = 'get'; 9 | Array.prototype.slice.call(form.querySelectorAll('input[type="submit"], input[type="image"], button:not([type]), button[type="submit"]')).forEach(function (submit) { 10 | submit.parentNode.insertBefore(document.createTextNode(' [GET]'), submit.nextSibling); 11 | }); 12 | }); 13 | 14 | /* Recurse for frames and iframes. */ 15 | try { 16 | Array.prototype.slice.call(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach(function (elem) { 17 | frmget(elem.contentDocument); 18 | }); 19 | } catch (e) { 20 | /* Catch exceptions for out-of-domain access, but do not do anything with them. */ 21 | } 22 | })(document); 23 | -------------------------------------------------------------------------------- /forms/frmpost.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Make all forms use POST. 3 | * 4 | * @title Form POST 5 | */ 6 | (function frmpost(document) { 7 | Array.prototype.slice.call(document.forms || document.getElementsByTagName('form')).forEach(function (form) { 8 | form.method = 'post'; 9 | Array.prototype.slice.call(form.querySelectorAll('input[type="submit"], input[type="image"], button:not([type]), button[type="submit"]')).forEach(function (submit) { 10 | submit.parentNode.insertBefore(document.createTextNode(' [POST]'), submit.nextSibling); 11 | }); 12 | }); 13 | 14 | /* Recurse for frames and iframes. */ 15 | try { 16 | Array.prototype.slice.call(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach(function (elem) { 17 | frmpost(elem.contentDocument); 18 | }); 19 | } catch (e) { 20 | /* Catch exceptions for out-of-domain access, but do not do anything with them. */ 21 | } 22 | })(document); 23 | -------------------------------------------------------------------------------- /forms/showpass.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Convert password inputs to normal text inputs and back again. 3 | * 4 | * @title Show passwords 5 | */ 6 | (function showpass(document) { 7 | Array.prototype.slice.call(document.querySelectorAll('input:not([type]), input[type="text"], input[type="password"]')).forEach(function (input) { 8 | if (input.type === 'password') { 9 | input.wasPassword = true; 10 | input.type = 'text'; 11 | } else if (input.wasPassword) { 12 | input.type = 'password'; 13 | } 14 | }); 15 | 16 | /* Recurse for frames and iframes. */ 17 | try { 18 | Array.prototype.slice.call(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach(function (elem) { 19 | showpass(elem.contentDocument); 20 | }); 21 | } catch (e) { 22 | /* Catch exceptions for out-of-domain access, but do not do anything with them. */ 23 | } 24 | })(document); 25 | -------------------------------------------------------------------------------- /language/README: -------------------------------------------------------------------------------- 1 | Language 2 | 3 | Dictionaries, translators, … 4 | -------------------------------------------------------------------------------- /language/dictionaries/README: -------------------------------------------------------------------------------- 1 | Dictionaries 2 | -------------------------------------------------------------------------------- /language/dictionaries/ac.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Arch Chinese dictionary. 3 | * 4 | * @title Arch Chinese 5 | */ 6 | (function ac() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the Arch Chinese dictionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://www.archchinese.com/chinese_english_dictionary.html?find=' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/dewikt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the German Wiktionary. 3 | * 4 | * @title German Wiktionary 5 | */ 6 | (function dewikt() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the German Wiktionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://de.wiktionary.org/wiki/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/dict.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text on Dictionary.com. 3 | * 4 | * @title Dictionary.com 5 | */ 6 | (function dict() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up on Dictionary.com:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://www.dictionary.com/browse/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/enwikt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the English Wiktionary. 3 | * 4 | * @title English Wiktionary 5 | */ 6 | (function enwikt() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the English Wiktionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://en.wiktionary.org/wiki/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/eswikt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Spanish Wiktionary. 3 | * 4 | * @title Spanish Wiktionary 5 | */ 6 | (function eswikt() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the Spanish Wiktionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://es.wiktionary.org/wiki/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/frwikt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the French Wiktionary. 3 | * 4 | * @title French Wiktionary 5 | */ 6 | (function frwikt() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the French Wiktionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://fr.wiktionary.org/wiki/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/itwikt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Italian Wiktionary. 3 | * 4 | * @title Italian Wiktionary 5 | */ 6 | (function itwikt() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the Italian Wiktionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://it.wiktionary.org/wiki/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/mw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text using Merriam-Webster. 3 | * 4 | * @title Merriam-Webster 5 | */ 6 | (function mw() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up using Merriam-Webster:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://www.merriam-webster.com/dictionary/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/nlwikt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Dutch Wiktionary. 3 | * 4 | * @title Dutch Wiktionary 5 | */ 6 | (function nlwikt() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the Dutch Wiktionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://nl.wiktionary.org/wiki/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/nowikt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Norwegian Wiktionary. 3 | * 4 | * @title Norwegian Wiktionary 5 | */ 6 | (function nowikt() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the Norwegian Wiktionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://no.wiktionary.org/wiki/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/oed.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Oxford English dictionary. 3 | * 4 | * @title O.E.D. 5 | */ 6 | (function oed() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the Oxford English dictionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://en.oxforddictionaries.com/definition/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/ptwikt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Portuguese Wiktionary. 3 | * 4 | * @title Portuguese Wiktionary 5 | */ 6 | (function ptwikt() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the Portuguese Wiktionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://pt.wiktionary.org/wiki/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/ruwikt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Russian Wiktionary. 3 | * 4 | * @title Russian Wiktionary 5 | */ 6 | (function ruwikt() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the Russian Wiktionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://ru.wiktionary.org/wiki/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/svwikt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Swedish Wiktionary. 3 | * 4 | * @title Swedish Wiktionary 5 | */ 6 | (function svwikt() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the Swedish Wiktionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://sv.wiktionary.org/wiki/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/thes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text on Thesaurus.com. 3 | * 4 | * @title Thesaurus.com 5 | */ 6 | (function thes() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up on Thesaurus.com:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://www.thesaurus.com/browse/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/ud.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Urban Dictionary. 3 | * 4 | * @title Urban Dictionary 5 | */ 6 | (function ud() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the Urban Dictionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://www.urbandictionary.com/define.php?term=' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/vd.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Dutch Van Dale dictionary. 3 | * 4 | * @title Van Dale 5 | */ 6 | (function vd() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the Dutch Van Dale dictionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://www.vandale.nl/gratis-woordenboek/nederlands/betekenis/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/viwikt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Vietnamese Wiktionary. 3 | * 4 | * @title Vietnamese Wiktionary 5 | */ 6 | (function viwikt() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the Vietnamese Wiktionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://vi.wiktionary.org/wiki/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/vw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the “Flemish” dictionary. 3 | * 4 | * @title Vlaams Woordenboek 5 | */ 6 | (function vw() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the “Flemish” dictionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://www.vlaamswoordenboek.be/definities/search?definition[q]=' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/dictionaries/zhwikt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the (Mandarin) Chinese Wiktionary. 3 | * 4 | * @title (Mandarin) Chinese Wiktionary 5 | */ 6 | (function zhwikt() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the word(s) to look up in the (Mandarin) Chinese Wiktionary:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://zh.wiktionary.org/wiki/' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /language/shuo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use Naver’s text-to-speech on the selected (Mandarin Chinese) text. 3 | * 4 | * @title Shuō (说) 5 | */ 6 | (function shuo() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the text to speak:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | var mp3Url = 'http://tts.cndic.naver.com/tts/mp3ttsV1.cgi?spk_id=250&text_fmt=0&pitch=100&volume=200&speed=80&wrapper=0&enc=0&text=' + encodeURI(s); 68 | new Audio(mp3Url).play().catch(error => alert('Could not play Naver’s text-to-speech. This can have many causes, including ad and tracker blockers.\n\nError: ' + error + '\n\nOriginal text:\n' + s)); 69 | } 70 | })(); 71 | -------------------------------------------------------------------------------- /language/translations/README: -------------------------------------------------------------------------------- 1 | Translators 2 | -------------------------------------------------------------------------------- /navigation/README: -------------------------------------------------------------------------------- 1 | Navigation 2 | 3 | Get around easier. 4 | -------------------------------------------------------------------------------- /navigation/back.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Go back to the previous page, or, failing that, to the referring page. 3 | * 4 | * @title Go back 5 | */ 6 | (function back() { 7 | if (history.length > 1) { 8 | history.back(); 9 | /* Go to the referrer when back() did not seem to have worked. */ 10 | window.setTimeout(goToReferrer, 250); 11 | } else { 12 | goToReferrer(); 13 | } 14 | 15 | function goToReferrer() { 16 | if (document.referrer) { 17 | location = document.referrer; 18 | } 19 | } 20 | })(); 21 | -------------------------------------------------------------------------------- /navigation/linktitles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Add the links' HREFs to their tooltips. 3 | * 4 | * @title Add link titles 5 | */ 6 | (function linktitles() { 7 | Array.prototype.slice.call(document.links).forEach(function (link) { 8 | if (link.title.indexOf(link.href) < 0) { 9 | link.title = link.title 10 | ? link.title + ' [' + link.href + ']' 11 | : link.href; 12 | } 13 | }); 14 | })(); 15 | -------------------------------------------------------------------------------- /navigation/refer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Go to the specified or selected URL using the current page as the referrer. 3 | * 4 | * @title Surf with referrer 5 | */ 6 | (function refer() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the destination URL:', location); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = s; 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /navigation/relink.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Replace all external links with new “A” elements with the same @href, 3 | * @class and inner HTML content, but without any previously attached event 4 | * handlers. 5 | * 6 | * Mainly useful for Google search result pages when trying to copy links 7 | * directly off those pages, so Google does not obfuscate them with tracker 8 | * garbage (which happens on click, before the link URL has been copied). 9 | * 10 | * @title Re-link 11 | */ 12 | (function relink() { 13 | 'use strict'; 14 | 15 | /* Recursively execute the logic on the document and its sub-documents. */ 16 | function execute(document) { 17 | let thisDomain = location.protocol + '//' + location.hostname + '/'; 18 | Array.from(document.querySelectorAll('a[href]:not([href^="/"]):not([href^="' + thisDomain + '"])')).forEach(a => { 19 | let newA = document.createElementNS('http://www.w3.org/1999/xhtml', 'a'); 20 | newA.href = a.href; 21 | newA.setAttribute('class', a.getAttribute('class')); 22 | newA.innerHTML = a.innerHTML; 23 | a.replaceWith(newA); 24 | console.log('relink: Replaced', a, 'with', newA); 25 | }); 26 | 27 | /* Recurse for (i)frames. */ 28 | try { 29 | Array.from(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach( 30 | elem => { try { execute(elem.contentDocument) } catch (e) { } } 31 | ); 32 | } catch (e) { 33 | /* Catch and ignore exceptions for out-of-domain access. */ 34 | } 35 | } 36 | 37 | execute(document); 38 | })(); 39 | -------------------------------------------------------------------------------- /navigation/rmh.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Remove the hash from the current page's URL, but keep the query string. 3 | * 4 | * @title Remove hash 5 | */ 6 | (function rmh() { 7 | var oldLocation = ('' + location); 8 | var newLocation = oldLocation.replace(/#.*/, ''); 9 | 10 | if (newLocation !== oldLocation) { 11 | /* See if the user wants to replace the URL without reloading. */ 12 | var s = (function () { /*%s*/; }).toString() 13 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 14 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 15 | .replace(/\u0025s/, ''); 16 | if ((' ' + s + ' ').match(/ --(no|skip)-reload /)) { 17 | history.pushState({}, document.title, newLocation); 18 | } else { 19 | location = newLocation; 20 | } 21 | } 22 | })(); 23 | -------------------------------------------------------------------------------- /navigation/rmhq.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Remove the hash and query string from the current page's URL. 3 | * 4 | * This is the same as "rmqh". 5 | * 6 | * @title Remove hash and query 7 | */ 8 | (function rmhq() { 9 | var oldLocation = ('' + location); 10 | var newLocation = oldLocation.replace(/[#?].*/, ''); 11 | 12 | if (newLocation !== oldLocation) { 13 | /* See if the user wants to replace the URL without reloading. */ 14 | var s = (function () { /*%s*/; }).toString() 15 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 16 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 17 | .replace(/\u0025s/, ''); 18 | if ((' ' + s + ' ').match(/ --(no|skip)-reload /)) { 19 | history.pushState({}, document.title, newLocation); 20 | } else { 21 | location = newLocation; 22 | } 23 | } 24 | })(); 25 | -------------------------------------------------------------------------------- /navigation/rmq.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Remove the query string from the current page's URL, but keep the hash. 3 | * 4 | * @title Remove query 5 | */ 6 | (function rmq() { 7 | var oldLocation = ('' + location); 8 | var newLocation = oldLocation.replace(/^([^#]*)\?[^#]*(#.*)?/, '$1$2'); 9 | 10 | if (newLocation !== oldLocation) { 11 | /* See if the user wants to replace the URL without reloading. */ 12 | var s = (function () { /*%s*/; }).toString() 13 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 14 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 15 | .replace(/\u0025s/, ''); 16 | if ((' ' + s + ' ').match(/ --(no|skip)-reload /)) { 17 | history.pushState({}, document.title, newLocation); 18 | } else { 19 | location = newLocation; 20 | } 21 | } 22 | })(); 23 | -------------------------------------------------------------------------------- /navigation/rmqh.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Remove the query string and hash from the current page's URL. 3 | * 4 | * This is the same as "rmhq". 5 | * 6 | * @title Remove query and hash 7 | */ 8 | (function rmqh() { 9 | var oldLocation = ('' + location); 10 | var newLocation = oldLocation.replace(/[#?].*/, ''); 11 | 12 | if (newLocation !== oldLocation) { 13 | /* See if the user wants to replace the URL without reloading. */ 14 | var s = (function () { /*%s*/; }).toString() 15 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 16 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 17 | .replace(/\u0025s/, ''); 18 | if ((' ' + s + ' ').match(/ --(no|skip)-reload /)) { 19 | history.pushState({}, document.title, newLocation); 20 | } else { 21 | location = newLocation; 22 | } 23 | } 24 | })(); 25 | -------------------------------------------------------------------------------- /navigation/root.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Go to the root directory, or the Nth directory starting from the root. 3 | * 4 | * For example, when you are on "https://www.example.com/foo/bar/baz": 5 | * - executing "/" goes to "https://www.example.com/" 6 | * - executing "/ 1" goes to "https://www.example.com/foo/" 7 | * - executing "/ 2" goes to "https://www.example.com/foo/bar/" 8 | * - executing "/ quux" goes to "https://www.example.com/quux" 9 | * 10 | * @title Go to the root 11 | * @keyword / 12 | */ 13 | (function () { 14 | /* Try to get the parameter string from the bookmarklet/search query. */ 15 | var s = (function () { /*%s*/; }).toString() 16 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 17 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 18 | .replace(/\u0025s/, ''); 19 | 20 | var level = parseInt(s, 10); 21 | if (s !== '') { 22 | if (isNaN(level)) { 23 | return location = '/' + s; 24 | } else { 25 | level = Math.max(0, level || 0); 26 | } 27 | } 28 | 29 | var parts = document.location.pathname.split('/'); 30 | var newParts = parts.slice(0, (level || 0) + 1); 31 | if (newParts.length < parts.length) { 32 | newParts.push(''); 33 | } 34 | location = newParts.join('/'); 35 | })(); 36 | -------------------------------------------------------------------------------- /navigation/unarchive.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Rewrite links to the Internet Archive Wayback Machine’s saved pages back to 3 | * the original URIs. 4 | * 5 | * @title Unarchive Wayback Machine links 6 | */ 7 | (function unarchive() { 8 | 'use strict'; 9 | 10 | /* Recursively execute the logic on the document and its sub-documents. */ 11 | function execute(document) { 12 | const isOnArchiveDotOrg = document.location.host === 'web.archive.org'; 13 | let wbASelectors; 14 | 15 | if (isOnArchiveDotOrg) { 16 | wbASelectors = [ 17 | /* The Wayback Machine does not rewrite *all* HREF attributes 18 | * in the HTML source. Relative HREFs are left alone, e.g. on 19 | * <https://web.archive.org/web/20210118051332/http://www.w3.org/2006/11/mwbp-tests/index.xhtml>: 20 | * 21 | * `<a href="https://web.archive.org/web/20210118051332/http://www.w3.org/2005/MWI/BPWG/techs/EncodingDeclarationSupport">` 22 | * 23 | * vs. 24 | * 25 | * `<a href="test-text-html.html">` 26 | * 27 | * So we have to check the HREF for *every* A element to get its 28 | * resolved URI. 29 | */ 30 | 'a[href]' 31 | ]; 32 | } else { 33 | wbASelectors = [ 34 | 'a[href^="//web.archive.org/web/"]', 35 | 'a[href^="http://web.archive.org/web/"]', 36 | 'a[href^="https://web.archive.org/web/"]' 37 | ]; 38 | } 39 | 40 | /* Wombat (the Wayback Machine’s JS library) dynamically rewrites URIs by 41 | * intercepting calls to `{get,set}Attribute('href')` and the `a.href` getter 42 | * and setter. However, calls to `setAttributeNS` are *not* intercepted. Take 43 | * advantage of the `a.href` interceptor that returns the resolved original URI 44 | * and use `setAttributeNS` to override the Wayback Machine’s rewritten HREF. 45 | * 46 | * On pages outside of web.archive.org, `a.href` returns the full archive.org 47 | * URI, so we have to strip it ourselves. 48 | */ 49 | Array.from(document.querySelectorAll(wbASelectors.join(', '))).forEach(a => { 50 | let originalUri = a.href; 51 | 52 | if (!isOnArchiveDotOrg) { 53 | originalUri = originalUri.replace(/^https?\:\/\/web.archive\.org\/web\/([^\/]+\/)?(https?:\/\/.*)/, '$2'); 54 | } 55 | 56 | a.setAttributeNS('', 'href', originalUri); 57 | }); 58 | 59 | /* Recurse for (i)frames. */ 60 | try { 61 | Array.from(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach( 62 | elem => { try { execute(elem.contentDocument) } catch (e) { } } 63 | ); 64 | } catch (e) { 65 | /* Catch and ignore exceptions for out-of-domain access. */ 66 | } 67 | } 68 | 69 | execute(document); 70 | })(); 71 | -------------------------------------------------------------------------------- /navigation/up.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Go to the parent "directory". Note that this function tries to be somewhat 3 | * smart about it: 4 | * - If the path ends in a slash, go to the parent directory. 5 | * - If the path looks like the default index, go to the parent directory and 6 | * use the same filename. 7 | * - For all other paths, go to the containing directory. 8 | * 9 | * For example: 10 | * - "/foo/bar/baz/" becomes "/foo/bar/" 11 | * - "/foo/bar/baz/index.html" becomes "/foo/bar/index.html" 12 | * - "/foo/bar/baz" becomes "/foo/bar/" (which in turn will become "/foo/") 13 | * 14 | * @title Go up 15 | * @keyword .. 16 | */ 17 | (function () { 18 | var parts = location.pathname.split('/'); 19 | if (parts.length === 2) { 20 | /* If we are in a leaf of the root, always go to "/". This way, */ 21 | /* /foo/index.html will go to /index.html, but /index.html will */ 22 | /* go to /. */ 23 | parts[parts.length - 1] = ''; 24 | } else if (parts[parts.length - 1] === '' || 25 | parts[parts.length - 1].match(/^(default|index)(\..*)?$/)) { 26 | /* Move the (posibly empty) index filename up one level. */ 27 | parts[parts.length - 2] = parts.pop(); 28 | } else { 29 | /* Remove the last leaf. */ 30 | parts[parts.length - 1] = ''; 31 | } 32 | location = parts.join('/'); 33 | })(); 34 | -------------------------------------------------------------------------------- /search/README: -------------------------------------------------------------------------------- 1 | Search engines 2 | -------------------------------------------------------------------------------- /search/bimg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search Bing Images. For reverse image search, specify the URL as the first parameter. 3 | * 4 | * @title Bing Images 5 | */ 6 | (function bimg() { 7 | 'use strict'; 8 | 9 | /* Try to get the parameter string from the bookmarklet/search query. 10 | * Fall back to the current text selection, if any. If those options 11 | * both fail, prompt the user. */ 12 | let s = (function () { /*%s*/; }).toString() 13 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 14 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 15 | .replace(/\u0025s/, ''); 16 | 17 | /** 18 | * Get the active text selection, diving into frames and 19 | * text controls when necessary and possible. 20 | */ 21 | function getActiveSelection(document) { 22 | if (!document || typeof document.getSelection !== 'function') { 23 | return ''; 24 | } 25 | 26 | if (!document.activeElement) { 27 | return document.getSelection() + ''; 28 | } 29 | 30 | const activeElement = document.activeElement; 31 | 32 | /* Recurse for FRAMEs and IFRAMEs. */ 33 | try { 34 | if ( 35 | typeof activeElement.contentDocument === 'object' 36 | && activeElement.contentDocument !== null 37 | ) { 38 | return getActiveSelection(activeElement.contentDocument); 39 | } 40 | } catch (e) { 41 | return document.getSelection() + ''; 42 | } 43 | 44 | /* Get the selection from inside a text control. */ 45 | if (typeof activeElement.value === 'string') { 46 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 47 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 48 | } 49 | 50 | return activeElement.value; 51 | } 52 | 53 | /* Get the normal selection. */ 54 | return document.getSelection() + ''; 55 | } 56 | 57 | if (s === '') { 58 | s = getActiveSelection(document) || prompt('Please enter your Bing Images search query or image URL:'); 59 | } else { 60 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection(document) + '$2'); 61 | } 62 | 63 | if (s) { 64 | /* If the parameter looks like a URL, use reverse image search. */ 65 | if (s.match(/^(\w+:(\/\/)?)?[^\s]+(\.[^\s]+)+\//)) 66 | { 67 | location = 'https://www.bing.com/images/search?view=detailv2&iss=sbi&FORM=SBIIRP&sbisrc=UrlPaste&q=imgurl:' + encodeURIComponent(s) + '&idpbck=1'; 68 | return; 69 | } 70 | 71 | location = 'https://www.bing.com/images/search?q=' + encodeURIComponent(s); 72 | } 73 | })(document); 74 | -------------------------------------------------------------------------------- /search/ddg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search DuckDuckGo for the given text, numbering the search result pages. 3 | * 4 | * @title DuckDuckGo 5 | */ 6 | (function ddg() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter your DuckDuckGo search query:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://duckduckgo.com/?ia=web&kv=1&q=' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /search/ddimg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search DuckDuckGo for images for the given text. 3 | * 4 | * @title DuckDuckGo image search 5 | */ 6 | (function ddimg(document) { 7 | 'use strict'; 8 | 9 | /* Try to get the parameter string from the bookmarklet/search query. 10 | * Fall back to the current text selection, if any. If those options 11 | * both fail, prompt the user. */ 12 | let s = (function () { /*%s*/; }).toString() 13 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 14 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 15 | .replace(/\u0025s/, ''); 16 | 17 | /** 18 | * Get the active text selection, diving into frames and 19 | * text controls when necessary and possible. 20 | */ 21 | function getActiveSelection(document) { 22 | if (!document || typeof document.getSelection !== 'function') { 23 | return ''; 24 | } 25 | 26 | if (!document.activeElement) { 27 | return document.getSelection() + ''; 28 | } 29 | 30 | const activeElement = document.activeElement; 31 | 32 | /* Recurse for FRAMEs and IFRAMEs. */ 33 | try { 34 | if ( 35 | typeof activeElement.contentDocument === 'object' 36 | && activeElement.contentDocument !== null 37 | ) { 38 | return getActiveSelection(activeElement.contentDocument); 39 | } 40 | } catch (e) { 41 | return document.getSelection() + ''; 42 | } 43 | 44 | /* Get the selection from inside a text control. */ 45 | if (typeof activeElement.value === 'string') { 46 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 47 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 48 | } 49 | 50 | return activeElement.value; 51 | } 52 | 53 | /* Get the normal selection. */ 54 | return document.getSelection() + ''; 55 | } 56 | 57 | if (s === '') { 58 | s = getActiveSelection(document) || prompt('Please enter your DuckDuckGo image search query:'); 59 | } else { 60 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection(document) + '$2'); 61 | } 62 | 63 | if (s) { 64 | location = 'https://duckduckgo.com/?ia=images&iax=images&q=' + encodeURIComponent(s); 65 | } 66 | })(document); 67 | -------------------------------------------------------------------------------- /search/e.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search using Ecosia, for “A better planet with every search”. 3 | * 4 | * @title Ecosia 5 | */ 6 | (function e(document) { 7 | 'use strict'; 8 | 9 | /* Try to get the parameter string from the bookmarklet/search query. 10 | * Fall back to the current text selection, if any. If those options 11 | * both fail, prompt the user. */ 12 | let s = (function () { /*%s*/; }).toString() 13 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 14 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 15 | .replace(/\u0025s/, ''); 16 | 17 | /** 18 | * Get the active text selection, diving into frames and 19 | * text controls when necessary and possible. 20 | */ 21 | function getActiveSelection(document) { 22 | if (!document || typeof document.getSelection !== 'function') { 23 | return ''; 24 | } 25 | 26 | if (!document.activeElement) { 27 | return document.getSelection() + ''; 28 | } 29 | 30 | const activeElement = document.activeElement; 31 | 32 | /* Recurse for FRAMEs and IFRAMEs. */ 33 | try { 34 | if ( 35 | typeof activeElement.contentDocument === 'object' 36 | && activeElement.contentDocument !== null 37 | ) { 38 | return getActiveSelection(activeElement.contentDocument); 39 | } 40 | } catch (e) { 41 | return document.getSelection() + ''; 42 | } 43 | 44 | /* Get the selection from inside a text control. */ 45 | if (typeof activeElement.value === 'string') { 46 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 47 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 48 | } 49 | 50 | return activeElement.value; 51 | } 52 | 53 | /* Get the normal selection. */ 54 | return document.getSelection() + ''; 55 | } 56 | 57 | if (s === '') { 58 | s = getActiveSelection(document) || prompt('Please enter your Ecosia web search query:'); 59 | } else { 60 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection(document) + '$2'); 61 | } 62 | 63 | if (s) { 64 | location = 'https://www.ecosia.org/search?q=' + encodeURIComponent(s); 65 | } 66 | })(document); 67 | -------------------------------------------------------------------------------- /search/eimg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search using Ecosia Images, for “A better planet with every search”. 3 | * 4 | * @title Ecosia Images 5 | */ 6 | (function eimg(document) { 7 | 'use strict'; 8 | 9 | /* Try to get the parameter string from the bookmarklet/search query. 10 | * Fall back to the current text selection, if any. If those options 11 | * both fail, prompt the user. */ 12 | let s = (function () { /*%s*/; }).toString() 13 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 14 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 15 | .replace(/\u0025s/, ''); 16 | 17 | /** 18 | * Get the active text selection, diving into frames and 19 | * text controls when necessary and possible. 20 | */ 21 | function getActiveSelection(document) { 22 | if (!document || typeof document.getSelection !== 'function') { 23 | return ''; 24 | } 25 | 26 | if (!document.activeElement) { 27 | return document.getSelection() + ''; 28 | } 29 | 30 | const activeElement = document.activeElement; 31 | 32 | /* Recurse for FRAMEs and IFRAMEs. */ 33 | try { 34 | if ( 35 | typeof activeElement.contentDocument === 'object' 36 | && activeElement.contentDocument !== null 37 | ) { 38 | return getActiveSelection(activeElement.contentDocument); 39 | } 40 | } catch (e) { 41 | return document.getSelection() + ''; 42 | } 43 | 44 | /* Get the selection from inside a text control. */ 45 | if (typeof activeElement.value === 'string') { 46 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 47 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 48 | } 49 | 50 | return activeElement.value; 51 | } 52 | 53 | /* Get the normal selection. */ 54 | return document.getSelection() + ''; 55 | } 56 | 57 | if (s === '') { 58 | s = getActiveSelection(document) || prompt('Please enter your Ecosia image search query:'); 59 | } else { 60 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection(document) + '$2'); 61 | } 62 | 63 | if (s) { 64 | location = 'https://www.ecosia.org/images?q=' + encodeURIComponent(s); 65 | } 66 | })(document); 67 | -------------------------------------------------------------------------------- /search/g.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search the international (English) Google for the given text, with a 100 3 | * results per SERP and without Instant Search. 4 | * 5 | * @title Google Search 6 | */ 7 | (function g() { 8 | /* Try to get the parameter string from the bookmarklet/search query. 9 | Fall back to the current text selection, if any. If those options 10 | both fail, prompt the user. 11 | */ 12 | var s = (function () { /*%s*/; }).toString() 13 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 14 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 15 | .replace(/\u0025s/, ''); 16 | 17 | /** 18 | * Get the active text selection, diving into frames and 19 | * text controls when necessary and possible. 20 | */ 21 | function getActiveSelection(doc) { 22 | if (arguments.length === 0) { 23 | doc = document; 24 | } 25 | 26 | if (!doc || typeof doc.getSelection !== 'function') { 27 | return ''; 28 | } 29 | 30 | if (!doc.activeElement) { 31 | return doc.getSelection() + ''; 32 | } 33 | 34 | var activeElement = doc.activeElement; 35 | 36 | /* Recurse for FRAMEs and IFRAMEs. */ 37 | try { 38 | if ( 39 | typeof activeElement.contentDocument === 'object' 40 | && activeElement.contentDocument !== null 41 | ) { 42 | return getActiveSelection(activeElement.contentDocument); 43 | } 44 | } catch (e) { 45 | return doc.getSelection() + ''; 46 | } 47 | 48 | /* Get the selection from inside a text control. */ 49 | if (typeof activeElement.value === 'string') { 50 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 51 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 52 | } 53 | 54 | return activeElement.value; 55 | } 56 | 57 | /* Get the normal selection. */ 58 | return doc.getSelection() + ''; 59 | } 60 | 61 | if (s === '') { 62 | s = getActiveSelection() || prompt('Please enter your Google search query:'); 63 | } else { 64 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 65 | } 66 | 67 | if (s) { 68 | location = 'https://www.google.com/search?ie=utf-8&hl=en&num=100&q=' + encodeURIComponent(s); 69 | } 70 | })(); 71 | -------------------------------------------------------------------------------- /search/gc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * View the Google cached text for the specified URL. 3 | * Specify "html" as the first parameter to view the cached HTML. 4 | * 5 | * @title Google cache 6 | */ 7 | (function gc() { 8 | /* Try to get the parameter string from the bookmarklet/search query. 9 | Fall back to the current text selection, if any. If those options 10 | both fail, prompt the user. 11 | */ 12 | var s = (function () { /*%s*/; }).toString() 13 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 14 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 15 | .replace(/\u0025s/, ''); 16 | 17 | /** 18 | * Get the active text selection, diving into frames and 19 | * text controls when necessary and possible. 20 | */ 21 | function getActiveSelection(doc) { 22 | if (arguments.length === 0) { 23 | doc = document; 24 | } 25 | 26 | if (!doc || typeof doc.getSelection !== 'function') { 27 | return ''; 28 | } 29 | 30 | if (!doc.activeElement) { 31 | return doc.getSelection() + ''; 32 | } 33 | 34 | var activeElement = doc.activeElement; 35 | 36 | /* Recurse for FRAMEs and IFRAMEs. */ 37 | try { 38 | if ( 39 | typeof activeElement.contentDocument === 'object' 40 | && activeElement.contentDocument !== null 41 | ) { 42 | return getActiveSelection(activeElement.contentDocument); 43 | } 44 | } catch (e) { 45 | return doc.getSelection() + ''; 46 | } 47 | 48 | /* Get the selection from inside a text control. */ 49 | if (typeof activeElement.value === 'string') { 50 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 51 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 52 | } 53 | 54 | return activeElement.value; 55 | } 56 | 57 | /* Get the normal selection. */ 58 | return doc.getSelection() + ''; 59 | } 60 | 61 | if (s === '') { 62 | s = getActiveSelection() || prompt('Please enter the URL whose Google cache to view:', location); 63 | } else { 64 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 65 | } 66 | 67 | if (s) { 68 | var url = 'https://webcache.googleusercontent.com/search?', words = s.split(' '); 69 | 70 | if (words[0] == 'html') { 71 | s = words.slice(1).join(' '); 72 | } else { 73 | url += 'strip=1&'; 74 | } 75 | 76 | location = url + 'q=cache:' + encodeURIComponent(s); 77 | } 78 | })(); 79 | -------------------------------------------------------------------------------- /search/go.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Go to the first DuckDuckGo result ("I'm Feeling Ducky") for the given text. 3 | * 4 | * @title Go 5 | */ 6 | (function go() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter your DuckDuckGo “I’m Feeling Ducky” search query:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | s += ' !'; 68 | location = 'https://www.duckduckgo.com/?q=' + encodeURIComponent(s); 69 | } 70 | })(); 71 | -------------------------------------------------------------------------------- /search/gs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search the international (English) Google for the given text on the current 3 | * site's domain, with a 100 results per SERP and without Instant Search. 4 | * 5 | * @title Google Site Search 6 | */ 7 | (function gs() { 8 | /* Try to get the parameter string from the bookmarklet/search query. 9 | Fall back to the current text selection, if any. If those options 10 | both fail, prompt the user. 11 | */ 12 | var s = (function () { /*%s*/; }).toString() 13 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 14 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 15 | .replace(/\u0025s/, ''); 16 | 17 | /** 18 | * Get the active text selection, diving into frames and 19 | * text controls when necessary and possible. 20 | */ 21 | function getActiveSelection(doc) { 22 | if (arguments.length === 0) { 23 | doc = document; 24 | } 25 | 26 | if (!doc || typeof doc.getSelection !== 'function') { 27 | return ''; 28 | } 29 | 30 | if (!doc.activeElement) { 31 | return doc.getSelection() + ''; 32 | } 33 | 34 | var activeElement = doc.activeElement; 35 | 36 | /* Recurse for FRAMEs and IFRAMEs. */ 37 | try { 38 | if ( 39 | typeof activeElement.contentDocument === 'object' 40 | && activeElement.contentDocument !== null 41 | ) { 42 | return getActiveSelection(activeElement.contentDocument); 43 | } 44 | } catch (e) { 45 | return doc.getSelection() + ''; 46 | } 47 | 48 | /* Get the selection from inside a text control. */ 49 | if (typeof activeElement.value === 'string') { 50 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 51 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 52 | } 53 | 54 | return activeElement.value; 55 | } 56 | 57 | /* Get the normal selection. */ 58 | return doc.getSelection() + ''; 59 | } 60 | 61 | if (s === '') { 62 | s = getActiveSelection() || prompt('Please enter your Google search query for ' + location.host + 't:'); 63 | } else { 64 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 65 | } 66 | 67 | if (s) { 68 | s = 'site:' + location.host + ' ' + s; 69 | location = 'https://www.google.com/search?ie=utf-8&hl=en&num=100&q=' + encodeURIComponent(s); 70 | } 71 | })(); 72 | -------------------------------------------------------------------------------- /search/img.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defaults to Ecosia Images (see `eimg`). 3 | * 4 | * @title Image search 5 | * @keyword img 6 | * @transclude eimg.js 7 | */ 8 | -------------------------------------------------------------------------------- /search/maps/README: -------------------------------------------------------------------------------- 1 | Maps & transportation 2 | -------------------------------------------------------------------------------- /search/maps/mapi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Go to the specified (lat, lng) coordinates with Mapillary. 3 | * 4 | * @title Mapillary 5 | */ 6 | (function mapi(document) { 7 | 'use strict'; 8 | 9 | /* Try to get the parameter string from the bookmarklet/search query. 10 | * Fall back to the current text selection, if any. If those options 11 | * both fail, prompt the user. */ 12 | let s = (function () { /*%s*/; }).toString() 13 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 14 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 15 | .replace(/\u0025s/, ''); 16 | 17 | /** 18 | * Get the active text selection, diving into frames and 19 | * text controls when necessary and possible. 20 | */ 21 | function getActiveSelection(document) { 22 | if (!document || typeof document.getSelection !== 'function') { 23 | return ''; 24 | } 25 | 26 | if (!document.activeElement) { 27 | return document.getSelection() + ''; 28 | } 29 | 30 | const activeElement = document.activeElement; 31 | 32 | /* Recurse for FRAMEs and IFRAMEs. */ 33 | try { 34 | if ( 35 | typeof activeElement.contentDocument === 'object' 36 | && activeElement.contentDocument !== null 37 | ) { 38 | return getActiveSelection(activeElement.contentDocument); 39 | } 40 | } catch (e) { 41 | return document.getSelection() + ''; 42 | } 43 | 44 | /* Get the selection from inside a text control. */ 45 | if (typeof activeElement.value === 'string') { 46 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 47 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 48 | } 49 | 50 | return activeElement.value; 51 | } 52 | 53 | /* Get the normal selection. */ 54 | return document.getSelection() + ''; 55 | } 56 | 57 | if (s === '') { 58 | s = getActiveSelection(document) || prompt('Specify the (lat, lng) coordinates:'); 59 | } else { 60 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection(document) + '$2'); 61 | } 62 | 63 | if (s) { 64 | const [lat, lng] = s 65 | .replace(/^\s*\(\s*/, '') 66 | .replace(/\s*\(\s*$/, '') 67 | .replace(/^https:\/\/www\.google\.com\/maps\/([^@]+\/)*@/, '') 68 | .split(/\s*[ ,]\s*/) 69 | .map(parseFloat); 70 | 71 | if (isNaN(lat) || isNaN(lng)) { 72 | alert(`Could not parse (lat, lng) coordinates from “${s}”`); 73 | return; 74 | } 75 | 76 | location = `https://www.mapillary.com/app/?lat=${encodeURIComponent(lat)}&lng=${encodeURIComponent(lng)}&z=15`; 77 | } 78 | })(document); 79 | -------------------------------------------------------------------------------- /search/tineye.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search images using the TinEye reverse image search. 3 | * 4 | * @title TinEye 5 | */ 6 | (function tin() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the image URL for TinEye to reverse search:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | if (!s.match(/^(\w+:(\/\/)?)?[^\s]+(\.[^\s]+)+\//)) 68 | { 69 | alert(`Are you sure that’s an image URL?\n\n${s}`); 70 | return; 71 | } 72 | 73 | location = 'https://tineye.com/search/?url=' + encodeURIComponent(s); 74 | } 75 | })(); 76 | -------------------------------------------------------------------------------- /search/tw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search Twitter for the specified text or URL. 3 | * 4 | * @title Twitter search 5 | */ 6 | (function tw() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter your Twitter search query:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://twitter.com/search?f=live&q=' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /search/tweep.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search Twitter for the specified user. 3 | * 4 | * @title Twitter user (Tweep) search 5 | */ 6 | (function tweep() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the Twitter user to search for:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://twitter.com/search?f=user&q=' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /search/wb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * View the Internet Archive Wayback Machine's latest cache for the specified URL. 3 | * 4 | * @title Wayback Machine 5 | */ 6 | (function wb() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the URL to look up in the Wayback Machine:', location); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | /* Typing “wb *\/example.com” (without the backslash) to search all 67 | * archived versions of “example.com” does not work because of the way 68 | * this bookmarklet wraps “percent s” in a multiline comment in a 69 | * function body (see above). As a workaround, use “wb * example.com” 70 | * or “wb 2010* example.com”. 71 | */ 72 | s = s.replace(/^\s*([0-9]+\*?|\*)\s+/, '$1/'); 73 | 74 | if (s) { 75 | location = 'https://web.archive.org/web/' + s; 76 | } 77 | })(); 78 | -------------------------------------------------------------------------------- /search/wikipedia/2comm.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Go to the corresponding Wikimedia Commons page for a Wikipedia article. 3 | * 4 | * @title Go to Wikimedia Commons 5 | * @keyword 2comm 6 | */ 7 | (function() { 8 | 'use strict'; 9 | 10 | /* Recursively execute the logic on the document and its sub-documents. */ 11 | function execute(document) { 12 | const commonsLink = document.querySelector('.wb-otherproject-link.wb-otherproject-commons a[href], .sistersitebox a[href^="https://commons.wikimedia.org/wiki/"], .sistersitebox a[href^="https://commons.m.wikimedia.org/wiki/"]'); 13 | if (commonsLink) { 14 | try { 15 | top.location = commonsLink.href; 16 | return; 17 | } catch (e) { 18 | location = commonsLink.href; 19 | } 20 | } 21 | 22 | /* Recurse for (i)frames. */ 23 | try { 24 | Array.from(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach( 25 | elem => { try { execute(elem.contentDocument) } catch (e) { } } 26 | ); 27 | } catch (e) { 28 | /* Catch and ignore exceptions for out-of-domain access. */ 29 | } 30 | } 31 | 32 | execute(document); 33 | })(); 34 | -------------------------------------------------------------------------------- /search/wikipedia/README: -------------------------------------------------------------------------------- 1 | Wikipedia 2 | -------------------------------------------------------------------------------- /search/wikipedia/anyw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in any Wikipedia. 3 | * 4 | * This does a Google "I'm feeling lucky" search for all wikipedia.org sites. 5 | * 6 | * @title Any Wikipedia 7 | */ 8 | (function anyw() { 9 | /* Try to get the parameter string from the bookmarklet/search query. 10 | Fall back to the current text selection, if any. If those options 11 | both fail, prompt the user. 12 | */ 13 | var s = (function () { /*%s*/; }).toString() 14 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 15 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 16 | .replace(/\u0025s/, ''); 17 | 18 | /** 19 | * Get the active text selection, diving into frames and 20 | * text controls when necessary and possible. 21 | */ 22 | function getActiveSelection(doc) { 23 | if (arguments.length === 0) { 24 | doc = document; 25 | } 26 | 27 | if (!doc || typeof doc.getSelection !== 'function') { 28 | return ''; 29 | } 30 | 31 | if (!doc.activeElement) { 32 | return doc.getSelection() + ''; 33 | } 34 | 35 | var activeElement = doc.activeElement; 36 | 37 | /* Recurse for FRAMEs and IFRAMEs. */ 38 | try { 39 | if ( 40 | typeof activeElement.contentDocument === 'object' 41 | && activeElement.contentDocument !== null 42 | ) { 43 | return getActiveSelection(activeElement.contentDocument); 44 | } 45 | } catch (e) { 46 | return doc.getSelection() + ''; 47 | } 48 | 49 | /* Get the selection from inside a text control. */ 50 | if (typeof activeElement.value === 'string') { 51 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 52 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 53 | } 54 | 55 | return activeElement.value; 56 | } 57 | 58 | /* Get the normal selection. */ 59 | return doc.getSelection() + ''; 60 | } 61 | 62 | if (s === '') { 63 | s = getActiveSelection() || prompt('Please enter the subject to look up on any Wikipedia:'); 64 | } else { 65 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 66 | } 67 | 68 | if (s) { 69 | /* Pro-tip: use this as keyword.URL in Firefox (see about:config). It used to be the default, but then Google slightly tweaked it. */ 70 | location = 'https://www.google.com/search?btnI=&ie=utf-8&sourceid=navclient&q=' + encodeURIComponent('site:wikipedia.org ' + s); 71 | } 72 | })(); 73 | -------------------------------------------------------------------------------- /search/wikipedia/dew.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the German Wikipedia. 3 | * 4 | * Use `--dis` as the first parameter to show the disambiguation page, if any. 5 | * 6 | * E.g. `enw --dis 1` would open `1 (disambiguation)`, which is a lot faster to 7 | * type then `enw 1 (disambiguation)`, and also works for the non-English 8 | * Wikipedia instances, which use other words for “disambiguation”. 9 | * 10 | * @title German Wikipedia 11 | * @keyword dew 12 | */ 13 | (function (config) { 14 | const {languageCode, languageNamesInEnglish, disambigationPageSuffix} = config; 15 | /* Try to get the parameter string from the bookmarklet/search query. 16 | Fall back to the current text selection, if any. If those options 17 | both fail, prompt the user. 18 | */ 19 | var s = (function () { /*%s*/; }).toString() 20 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 21 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 22 | .replace(/\u0025s/, ''); 23 | 24 | /** 25 | * Get the active text selection, diving into frames and 26 | * text controls when necessary and possible. 27 | */ 28 | function getActiveSelection(doc) { 29 | if (arguments.length === 0) { 30 | doc = document; 31 | } 32 | 33 | if (!doc || typeof doc.getSelection !== 'function') { 34 | return ''; 35 | } 36 | 37 | if (!doc.activeElement) { 38 | return doc.getSelection() + ''; 39 | } 40 | 41 | var activeElement = doc.activeElement; 42 | 43 | /* Recurse for FRAMEs and IFRAMEs. */ 44 | try { 45 | if ( 46 | typeof activeElement.contentDocument === 'object' 47 | && activeElement.contentDocument !== null 48 | ) { 49 | return getActiveSelection(activeElement.contentDocument); 50 | } 51 | } catch (e) { 52 | return doc.getSelection() + ''; 53 | } 54 | 55 | /* Get the selection from inside a text control. */ 56 | if (typeof activeElement.value === 'string') { 57 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 58 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 59 | } 60 | 61 | return activeElement.value; 62 | } 63 | 64 | /* Get the normal selection. */ 65 | return doc.getSelection() + ''; 66 | } 67 | 68 | if (s === '') { 69 | s = getActiveSelection() || prompt(`Please enter the subject to look up in the ${languageNamesInEnglish.join('/')} Wikipedia:`); 70 | } else { 71 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 72 | } 73 | 74 | if (s) { 75 | /* Open the disambigation page for queries like `enw --dis Foo`. */ 76 | let matches = s.match(/^\s*--dis\s*(.+)/); 77 | if (matches) { 78 | s = matches[1] + disambigationPageSuffix; 79 | } 80 | 81 | /* The Wikipedia search works like "I'm feeling lucky" on most Wikipedia instances. If there is a complete match, it will redirect us there. */ 82 | location = `https://${languageCode}.wikipedia.org/w/index.php?searchToken=.&title=Special%3ASearch&ns0=1&search=${encodeURIComponent(s)}`; 83 | } 84 | })({ 85 | 86 | languageCode: 'de', 87 | languageNamesInEnglish: ['German'], 88 | disambigationPageSuffix: ' (Begriffsklärung)', 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /search/wikipedia/enw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the English Wikipedia. 3 | * 4 | * Use `--dis` as the first parameter to show the disambiguation page, if any. 5 | * 6 | * E.g. `enw --dis 1` would open `1 (disambiguation)`, which is a lot faster to 7 | * type then `enw 1 (disambiguation)`, and also works for the non-English 8 | * Wikipedia instances, which use other words for “disambiguation”. 9 | * 10 | * @title English Wikipedia 11 | * @keyword enw 12 | */ 13 | (function (config) { 14 | const {languageCode, languageNamesInEnglish, disambigationPageSuffix} = config; 15 | /* Try to get the parameter string from the bookmarklet/search query. 16 | Fall back to the current text selection, if any. If those options 17 | both fail, prompt the user. 18 | */ 19 | var s = (function () { /*%s*/; }).toString() 20 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 21 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 22 | .replace(/\u0025s/, ''); 23 | 24 | /** 25 | * Get the active text selection, diving into frames and 26 | * text controls when necessary and possible. 27 | */ 28 | function getActiveSelection(doc) { 29 | if (arguments.length === 0) { 30 | doc = document; 31 | } 32 | 33 | if (!doc || typeof doc.getSelection !== 'function') { 34 | return ''; 35 | } 36 | 37 | if (!doc.activeElement) { 38 | return doc.getSelection() + ''; 39 | } 40 | 41 | var activeElement = doc.activeElement; 42 | 43 | /* Recurse for FRAMEs and IFRAMEs. */ 44 | try { 45 | if ( 46 | typeof activeElement.contentDocument === 'object' 47 | && activeElement.contentDocument !== null 48 | ) { 49 | return getActiveSelection(activeElement.contentDocument); 50 | } 51 | } catch (e) { 52 | return doc.getSelection() + ''; 53 | } 54 | 55 | /* Get the selection from inside a text control. */ 56 | if (typeof activeElement.value === 'string') { 57 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 58 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 59 | } 60 | 61 | return activeElement.value; 62 | } 63 | 64 | /* Get the normal selection. */ 65 | return doc.getSelection() + ''; 66 | } 67 | 68 | if (s === '') { 69 | s = getActiveSelection() || prompt(`Please enter the subject to look up in the ${languageNamesInEnglish.join('/')} Wikipedia:`); 70 | } else { 71 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 72 | } 73 | 74 | if (s) { 75 | /* Open the disambigation page for queries like `enw --dis Foo`. */ 76 | let matches = s.match(/^\s*--dis\s*(.+)/); 77 | if (matches) { 78 | s = matches[1] + disambigationPageSuffix; 79 | } 80 | 81 | /* The Wikipedia search works like "I'm feeling lucky" on most Wikipedia instances. If there is a complete match, it will redirect us there. */ 82 | location = `https://${languageCode}.wikipedia.org/w/index.php?searchToken=.&title=Special%3ASearch&ns0=1&search=${encodeURIComponent(s)}`; 83 | } 84 | })({ 85 | /* ↓ XXX Begin JS config object ↓ XXX */ 86 | languageCode: 'en', 87 | languageNamesInEnglish: ['English'], 88 | disambigationPageSuffix: ' (disambiguation)', 89 | /* ↑ XXX End JS config object ↑ XXX */ 90 | }); 91 | -------------------------------------------------------------------------------- /search/wikipedia/esw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Spanish Wikipedia. 3 | * 4 | * Use `--dis` as the first parameter to show the disambiguation page, if any. 5 | * 6 | * E.g. `enw --dis 1` would open `1 (disambiguation)`, which is a lot faster to 7 | * type then `enw 1 (disambiguation)`, and also works for the non-English 8 | * Wikipedia instances, which use other words for “disambiguation”. 9 | * 10 | * @title Spanish Wikipedia 11 | * @keyword esw 12 | */ 13 | (function (config) { 14 | const {languageCode, languageNamesInEnglish, disambigationPageSuffix} = config; 15 | /* Try to get the parameter string from the bookmarklet/search query. 16 | Fall back to the current text selection, if any. If those options 17 | both fail, prompt the user. 18 | */ 19 | var s = (function () { /*%s*/; }).toString() 20 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 21 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 22 | .replace(/\u0025s/, ''); 23 | 24 | /** 25 | * Get the active text selection, diving into frames and 26 | * text controls when necessary and possible. 27 | */ 28 | function getActiveSelection(doc) { 29 | if (arguments.length === 0) { 30 | doc = document; 31 | } 32 | 33 | if (!doc || typeof doc.getSelection !== 'function') { 34 | return ''; 35 | } 36 | 37 | if (!doc.activeElement) { 38 | return doc.getSelection() + ''; 39 | } 40 | 41 | var activeElement = doc.activeElement; 42 | 43 | /* Recurse for FRAMEs and IFRAMEs. */ 44 | try { 45 | if ( 46 | typeof activeElement.contentDocument === 'object' 47 | && activeElement.contentDocument !== null 48 | ) { 49 | return getActiveSelection(activeElement.contentDocument); 50 | } 51 | } catch (e) { 52 | return doc.getSelection() + ''; 53 | } 54 | 55 | /* Get the selection from inside a text control. */ 56 | if (typeof activeElement.value === 'string') { 57 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 58 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 59 | } 60 | 61 | return activeElement.value; 62 | } 63 | 64 | /* Get the normal selection. */ 65 | return doc.getSelection() + ''; 66 | } 67 | 68 | if (s === '') { 69 | s = getActiveSelection() || prompt(`Please enter the subject to look up in the ${languageNamesInEnglish.join('/')} Wikipedia:`); 70 | } else { 71 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 72 | } 73 | 74 | if (s) { 75 | /* Open the disambigation page for queries like `enw --dis Foo`. */ 76 | let matches = s.match(/^\s*--dis\s*(.+)/); 77 | if (matches) { 78 | s = matches[1] + disambigationPageSuffix; 79 | } 80 | 81 | /* The Wikipedia search works like "I'm feeling lucky" on most Wikipedia instances. If there is a complete match, it will redirect us there. */ 82 | location = `https://${languageCode}.wikipedia.org/w/index.php?searchToken=.&title=Special%3ASearch&ns0=1&search=${encodeURIComponent(s)}`; 83 | } 84 | })({ 85 | 86 | languageCode: 'es', 87 | languageNamesInEnglish: ['Spanish'], 88 | disambigationPageSuffix: ' (desambiguación)', 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /search/wikipedia/frw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the French Wikipedia. 3 | * 4 | * Use `--dis` as the first parameter to show the disambiguation page, if any. 5 | * 6 | * E.g. `enw --dis 1` would open `1 (disambiguation)`, which is a lot faster to 7 | * type then `enw 1 (disambiguation)`, and also works for the non-English 8 | * Wikipedia instances, which use other words for “disambiguation”. 9 | * 10 | * @title French Wikipedia 11 | * @keyword frw 12 | */ 13 | (function (config) { 14 | const {languageCode, languageNamesInEnglish, disambigationPageSuffix} = config; 15 | /* Try to get the parameter string from the bookmarklet/search query. 16 | Fall back to the current text selection, if any. If those options 17 | both fail, prompt the user. 18 | */ 19 | var s = (function () { /*%s*/; }).toString() 20 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 21 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 22 | .replace(/\u0025s/, ''); 23 | 24 | /** 25 | * Get the active text selection, diving into frames and 26 | * text controls when necessary and possible. 27 | */ 28 | function getActiveSelection(doc) { 29 | if (arguments.length === 0) { 30 | doc = document; 31 | } 32 | 33 | if (!doc || typeof doc.getSelection !== 'function') { 34 | return ''; 35 | } 36 | 37 | if (!doc.activeElement) { 38 | return doc.getSelection() + ''; 39 | } 40 | 41 | var activeElement = doc.activeElement; 42 | 43 | /* Recurse for FRAMEs and IFRAMEs. */ 44 | try { 45 | if ( 46 | typeof activeElement.contentDocument === 'object' 47 | && activeElement.contentDocument !== null 48 | ) { 49 | return getActiveSelection(activeElement.contentDocument); 50 | } 51 | } catch (e) { 52 | return doc.getSelection() + ''; 53 | } 54 | 55 | /* Get the selection from inside a text control. */ 56 | if (typeof activeElement.value === 'string') { 57 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 58 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 59 | } 60 | 61 | return activeElement.value; 62 | } 63 | 64 | /* Get the normal selection. */ 65 | return doc.getSelection() + ''; 66 | } 67 | 68 | if (s === '') { 69 | s = getActiveSelection() || prompt(`Please enter the subject to look up in the ${languageNamesInEnglish.join('/')} Wikipedia:`); 70 | } else { 71 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 72 | } 73 | 74 | if (s) { 75 | /* Open the disambigation page for queries like `enw --dis Foo`. */ 76 | let matches = s.match(/^\s*--dis\s*(.+)/); 77 | if (matches) { 78 | s = matches[1] + disambigationPageSuffix; 79 | } 80 | 81 | /* The Wikipedia search works like "I'm feeling lucky" on most Wikipedia instances. If there is a complete match, it will redirect us there. */ 82 | location = `https://${languageCode}.wikipedia.org/w/index.php?searchToken=.&title=Special%3ASearch&ns0=1&search=${encodeURIComponent(s)}`; 83 | } 84 | })({ 85 | 86 | languageCode: 'fr', 87 | languageNamesInEnglish: ['French'], 88 | disambigationPageSuffix: ' (homonymie)', 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /search/wikipedia/itw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Italian Wikipedia. 3 | * 4 | * Use `--dis` as the first parameter to show the disambiguation page, if any. 5 | * 6 | * E.g. `enw --dis 1` would open `1 (disambiguation)`, which is a lot faster to 7 | * type then `enw 1 (disambiguation)`, and also works for the non-English 8 | * Wikipedia instances, which use other words for “disambiguation”. 9 | * 10 | * @title Italian Wikipedia 11 | * @keyword itw 12 | */ 13 | (function (config) { 14 | const {languageCode, languageNamesInEnglish, disambigationPageSuffix} = config; 15 | /* Try to get the parameter string from the bookmarklet/search query. 16 | Fall back to the current text selection, if any. If those options 17 | both fail, prompt the user. 18 | */ 19 | var s = (function () { /*%s*/; }).toString() 20 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 21 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 22 | .replace(/\u0025s/, ''); 23 | 24 | /** 25 | * Get the active text selection, diving into frames and 26 | * text controls when necessary and possible. 27 | */ 28 | function getActiveSelection(doc) { 29 | if (arguments.length === 0) { 30 | doc = document; 31 | } 32 | 33 | if (!doc || typeof doc.getSelection !== 'function') { 34 | return ''; 35 | } 36 | 37 | if (!doc.activeElement) { 38 | return doc.getSelection() + ''; 39 | } 40 | 41 | var activeElement = doc.activeElement; 42 | 43 | /* Recurse for FRAMEs and IFRAMEs. */ 44 | try { 45 | if ( 46 | typeof activeElement.contentDocument === 'object' 47 | && activeElement.contentDocument !== null 48 | ) { 49 | return getActiveSelection(activeElement.contentDocument); 50 | } 51 | } catch (e) { 52 | return doc.getSelection() + ''; 53 | } 54 | 55 | /* Get the selection from inside a text control. */ 56 | if (typeof activeElement.value === 'string') { 57 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 58 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 59 | } 60 | 61 | return activeElement.value; 62 | } 63 | 64 | /* Get the normal selection. */ 65 | return doc.getSelection() + ''; 66 | } 67 | 68 | if (s === '') { 69 | s = getActiveSelection() || prompt(`Please enter the subject to look up in the ${languageNamesInEnglish.join('/')} Wikipedia:`); 70 | } else { 71 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 72 | } 73 | 74 | if (s) { 75 | /* Open the disambigation page for queries like `enw --dis Foo`. */ 76 | let matches = s.match(/^\s*--dis\s*(.+)/); 77 | if (matches) { 78 | s = matches[1] + disambigationPageSuffix; 79 | } 80 | 81 | /* The Wikipedia search works like "I'm feeling lucky" on most Wikipedia instances. If there is a complete match, it will redirect us there. */ 82 | location = `https://${languageCode}.wikipedia.org/w/index.php?searchToken=.&title=Special%3ASearch&ns0=1&search=${encodeURIComponent(s)}`; 83 | } 84 | })({ 85 | 86 | languageCode: 'it', 87 | languageNamesInEnglish: ['Italian'], 88 | disambigationPageSuffix: ' (disambigua)', 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /search/wikipedia/nlw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Dutch Wikipedia. 3 | * 4 | * Use `--dis` as the first parameter to show the disambiguation page, if any. 5 | * 6 | * E.g. `enw --dis 1` would open `1 (disambiguation)`, which is a lot faster to 7 | * type then `enw 1 (disambiguation)`, and also works for the non-English 8 | * Wikipedia instances, which use other words for “disambiguation”. 9 | * 10 | * @title Dutch Wikipedia 11 | * @keyword nlw 12 | */ 13 | (function (config) { 14 | const {languageCode, languageNamesInEnglish, disambigationPageSuffix} = config; 15 | /* Try to get the parameter string from the bookmarklet/search query. 16 | Fall back to the current text selection, if any. If those options 17 | both fail, prompt the user. 18 | */ 19 | var s = (function () { /*%s*/; }).toString() 20 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 21 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 22 | .replace(/\u0025s/, ''); 23 | 24 | /** 25 | * Get the active text selection, diving into frames and 26 | * text controls when necessary and possible. 27 | */ 28 | function getActiveSelection(doc) { 29 | if (arguments.length === 0) { 30 | doc = document; 31 | } 32 | 33 | if (!doc || typeof doc.getSelection !== 'function') { 34 | return ''; 35 | } 36 | 37 | if (!doc.activeElement) { 38 | return doc.getSelection() + ''; 39 | } 40 | 41 | var activeElement = doc.activeElement; 42 | 43 | /* Recurse for FRAMEs and IFRAMEs. */ 44 | try { 45 | if ( 46 | typeof activeElement.contentDocument === 'object' 47 | && activeElement.contentDocument !== null 48 | ) { 49 | return getActiveSelection(activeElement.contentDocument); 50 | } 51 | } catch (e) { 52 | return doc.getSelection() + ''; 53 | } 54 | 55 | /* Get the selection from inside a text control. */ 56 | if (typeof activeElement.value === 'string') { 57 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 58 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 59 | } 60 | 61 | return activeElement.value; 62 | } 63 | 64 | /* Get the normal selection. */ 65 | return doc.getSelection() + ''; 66 | } 67 | 68 | if (s === '') { 69 | s = getActiveSelection() || prompt(`Please enter the subject to look up in the ${languageNamesInEnglish.join('/')} Wikipedia:`); 70 | } else { 71 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 72 | } 73 | 74 | if (s) { 75 | /* Open the disambigation page for queries like `enw --dis Foo`. */ 76 | let matches = s.match(/^\s*--dis\s*(.+)/); 77 | if (matches) { 78 | s = matches[1] + disambigationPageSuffix; 79 | } 80 | 81 | /* The Wikipedia search works like "I'm feeling lucky" on most Wikipedia instances. If there is a complete match, it will redirect us there. */ 82 | location = `https://${languageCode}.wikipedia.org/w/index.php?searchToken=.&title=Special%3ASearch&ns0=1&search=${encodeURIComponent(s)}`; 83 | } 84 | })({ 85 | 86 | languageCode: 'nl', 87 | languageNamesInEnglish: ['Dutch'], 88 | disambigationPageSuffix: ' (doorverwijspagina)', 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /search/wikipedia/now.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Norwegian Wikipedia. 3 | * 4 | * Use `--dis` as the first parameter to show the disambiguation page, if any. 5 | * 6 | * E.g. `enw --dis 1` would open `1 (disambiguation)`, which is a lot faster to 7 | * type then `enw 1 (disambiguation)`, and also works for the non-English 8 | * Wikipedia instances, which use other words for “disambiguation”. 9 | * 10 | * @title Norwegian Wikipedia 11 | * @keyword now 12 | */ 13 | (function (config) { 14 | const {languageCode, languageNamesInEnglish, disambigationPageSuffix} = config; 15 | /* Try to get the parameter string from the bookmarklet/search query. 16 | Fall back to the current text selection, if any. If those options 17 | both fail, prompt the user. 18 | */ 19 | var s = (function () { /*%s*/; }).toString() 20 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 21 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 22 | .replace(/\u0025s/, ''); 23 | 24 | /** 25 | * Get the active text selection, diving into frames and 26 | * text controls when necessary and possible. 27 | */ 28 | function getActiveSelection(doc) { 29 | if (arguments.length === 0) { 30 | doc = document; 31 | } 32 | 33 | if (!doc || typeof doc.getSelection !== 'function') { 34 | return ''; 35 | } 36 | 37 | if (!doc.activeElement) { 38 | return doc.getSelection() + ''; 39 | } 40 | 41 | var activeElement = doc.activeElement; 42 | 43 | /* Recurse for FRAMEs and IFRAMEs. */ 44 | try { 45 | if ( 46 | typeof activeElement.contentDocument === 'object' 47 | && activeElement.contentDocument !== null 48 | ) { 49 | return getActiveSelection(activeElement.contentDocument); 50 | } 51 | } catch (e) { 52 | return doc.getSelection() + ''; 53 | } 54 | 55 | /* Get the selection from inside a text control. */ 56 | if (typeof activeElement.value === 'string') { 57 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 58 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 59 | } 60 | 61 | return activeElement.value; 62 | } 63 | 64 | /* Get the normal selection. */ 65 | return doc.getSelection() + ''; 66 | } 67 | 68 | if (s === '') { 69 | s = getActiveSelection() || prompt(`Please enter the subject to look up in the ${languageNamesInEnglish.join('/')} Wikipedia:`); 70 | } else { 71 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 72 | } 73 | 74 | if (s) { 75 | /* Open the disambigation page for queries like `enw --dis Foo`. */ 76 | let matches = s.match(/^\s*--dis\s*(.+)/); 77 | if (matches) { 78 | s = matches[1] + disambigationPageSuffix; 79 | } 80 | 81 | /* The Wikipedia search works like "I'm feeling lucky" on most Wikipedia instances. If there is a complete match, it will redirect us there. */ 82 | location = `https://${languageCode}.wikipedia.org/w/index.php?searchToken=.&title=Special%3ASearch&ns0=1&search=${encodeURIComponent(s)}`; 83 | } 84 | })({ 85 | 86 | languageCode: 'no', 87 | languageNamesInEnglish: ['Norwegian'], 88 | disambigationPageSuffix: ' (andre betydninger)', 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /search/wikipedia/ptw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Portuguese Wikipedia. 3 | * 4 | * Use `--dis` as the first parameter to show the disambiguation page, if any. 5 | * 6 | * E.g. `enw --dis 1` would open `1 (disambiguation)`, which is a lot faster to 7 | * type then `enw 1 (disambiguation)`, and also works for the non-English 8 | * Wikipedia instances, which use other words for “disambiguation”. 9 | * 10 | * @title Portuguese Wikipedia 11 | * @keyword ptw 12 | */ 13 | (function (config) { 14 | const {languageCode, languageNamesInEnglish, disambigationPageSuffix} = config; 15 | /* Try to get the parameter string from the bookmarklet/search query. 16 | Fall back to the current text selection, if any. If those options 17 | both fail, prompt the user. 18 | */ 19 | var s = (function () { /*%s*/; }).toString() 20 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 21 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 22 | .replace(/\u0025s/, ''); 23 | 24 | /** 25 | * Get the active text selection, diving into frames and 26 | * text controls when necessary and possible. 27 | */ 28 | function getActiveSelection(doc) { 29 | if (arguments.length === 0) { 30 | doc = document; 31 | } 32 | 33 | if (!doc || typeof doc.getSelection !== 'function') { 34 | return ''; 35 | } 36 | 37 | if (!doc.activeElement) { 38 | return doc.getSelection() + ''; 39 | } 40 | 41 | var activeElement = doc.activeElement; 42 | 43 | /* Recurse for FRAMEs and IFRAMEs. */ 44 | try { 45 | if ( 46 | typeof activeElement.contentDocument === 'object' 47 | && activeElement.contentDocument !== null 48 | ) { 49 | return getActiveSelection(activeElement.contentDocument); 50 | } 51 | } catch (e) { 52 | return doc.getSelection() + ''; 53 | } 54 | 55 | /* Get the selection from inside a text control. */ 56 | if (typeof activeElement.value === 'string') { 57 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 58 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 59 | } 60 | 61 | return activeElement.value; 62 | } 63 | 64 | /* Get the normal selection. */ 65 | return doc.getSelection() + ''; 66 | } 67 | 68 | if (s === '') { 69 | s = getActiveSelection() || prompt(`Please enter the subject to look up in the ${languageNamesInEnglish.join('/')} Wikipedia:`); 70 | } else { 71 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 72 | } 73 | 74 | if (s) { 75 | /* Open the disambigation page for queries like `enw --dis Foo`. */ 76 | let matches = s.match(/^\s*--dis\s*(.+)/); 77 | if (matches) { 78 | s = matches[1] + disambigationPageSuffix; 79 | } 80 | 81 | /* The Wikipedia search works like "I'm feeling lucky" on most Wikipedia instances. If there is a complete match, it will redirect us there. */ 82 | location = `https://${languageCode}.wikipedia.org/w/index.php?searchToken=.&title=Special%3ASearch&ns0=1&search=${encodeURIComponent(s)}`; 83 | } 84 | })({ 85 | 86 | languageCode: 'pt', 87 | languageNamesInEnglish: ['Portuguese'], 88 | disambigationPageSuffix: ' (desambiguação)', 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /search/wikipedia/ruw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Russian Wikipedia. 3 | * 4 | * Use `--dis` as the first parameter to show the disambiguation page, if any. 5 | * 6 | * E.g. `enw --dis 1` would open `1 (disambiguation)`, which is a lot faster to 7 | * type then `enw 1 (disambiguation)`, and also works for the non-English 8 | * Wikipedia instances, which use other words for “disambiguation”. 9 | * 10 | * @title Russian Wikipedia 11 | * @keyword ruw 12 | */ 13 | (function (config) { 14 | const {languageCode, languageNamesInEnglish, disambigationPageSuffix} = config; 15 | /* Try to get the parameter string from the bookmarklet/search query. 16 | Fall back to the current text selection, if any. If those options 17 | both fail, prompt the user. 18 | */ 19 | var s = (function () { /*%s*/; }).toString() 20 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 21 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 22 | .replace(/\u0025s/, ''); 23 | 24 | /** 25 | * Get the active text selection, diving into frames and 26 | * text controls when necessary and possible. 27 | */ 28 | function getActiveSelection(doc) { 29 | if (arguments.length === 0) { 30 | doc = document; 31 | } 32 | 33 | if (!doc || typeof doc.getSelection !== 'function') { 34 | return ''; 35 | } 36 | 37 | if (!doc.activeElement) { 38 | return doc.getSelection() + ''; 39 | } 40 | 41 | var activeElement = doc.activeElement; 42 | 43 | /* Recurse for FRAMEs and IFRAMEs. */ 44 | try { 45 | if ( 46 | typeof activeElement.contentDocument === 'object' 47 | && activeElement.contentDocument !== null 48 | ) { 49 | return getActiveSelection(activeElement.contentDocument); 50 | } 51 | } catch (e) { 52 | return doc.getSelection() + ''; 53 | } 54 | 55 | /* Get the selection from inside a text control. */ 56 | if (typeof activeElement.value === 'string') { 57 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 58 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 59 | } 60 | 61 | return activeElement.value; 62 | } 63 | 64 | /* Get the normal selection. */ 65 | return doc.getSelection() + ''; 66 | } 67 | 68 | if (s === '') { 69 | s = getActiveSelection() || prompt(`Please enter the subject to look up in the ${languageNamesInEnglish.join('/')} Wikipedia:`); 70 | } else { 71 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 72 | } 73 | 74 | if (s) { 75 | /* Open the disambigation page for queries like `enw --dis Foo`. */ 76 | let matches = s.match(/^\s*--dis\s*(.+)/); 77 | if (matches) { 78 | s = matches[1] + disambigationPageSuffix; 79 | } 80 | 81 | /* The Wikipedia search works like "I'm feeling lucky" on most Wikipedia instances. If there is a complete match, it will redirect us there. */ 82 | location = `https://${languageCode}.wikipedia.org/w/index.php?searchToken=.&title=Special%3ASearch&ns0=1&search=${encodeURIComponent(s)}`; 83 | } 84 | })({ 85 | 86 | languageCode: 'ru', 87 | languageNamesInEnglish: ['Russian'], 88 | disambigationPageSuffix: ' (значения)', 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /search/wikipedia/svw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Swedish Wikipedia. 3 | * 4 | * Use `--dis` as the first parameter to show the disambiguation page, if any. 5 | * 6 | * E.g. `enw --dis 1` would open `1 (disambiguation)`, which is a lot faster to 7 | * type then `enw 1 (disambiguation)`, and also works for the non-English 8 | * Wikipedia instances, which use other words for “disambiguation”. 9 | * 10 | * @title Swedish Wikipedia 11 | * @keyword svw 12 | */ 13 | (function (config) { 14 | const {languageCode, languageNamesInEnglish, disambigationPageSuffix} = config; 15 | /* Try to get the parameter string from the bookmarklet/search query. 16 | Fall back to the current text selection, if any. If those options 17 | both fail, prompt the user. 18 | */ 19 | var s = (function () { /*%s*/; }).toString() 20 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 21 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 22 | .replace(/\u0025s/, ''); 23 | 24 | /** 25 | * Get the active text selection, diving into frames and 26 | * text controls when necessary and possible. 27 | */ 28 | function getActiveSelection(doc) { 29 | if (arguments.length === 0) { 30 | doc = document; 31 | } 32 | 33 | if (!doc || typeof doc.getSelection !== 'function') { 34 | return ''; 35 | } 36 | 37 | if (!doc.activeElement) { 38 | return doc.getSelection() + ''; 39 | } 40 | 41 | var activeElement = doc.activeElement; 42 | 43 | /* Recurse for FRAMEs and IFRAMEs. */ 44 | try { 45 | if ( 46 | typeof activeElement.contentDocument === 'object' 47 | && activeElement.contentDocument !== null 48 | ) { 49 | return getActiveSelection(activeElement.contentDocument); 50 | } 51 | } catch (e) { 52 | return doc.getSelection() + ''; 53 | } 54 | 55 | /* Get the selection from inside a text control. */ 56 | if (typeof activeElement.value === 'string') { 57 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 58 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 59 | } 60 | 61 | return activeElement.value; 62 | } 63 | 64 | /* Get the normal selection. */ 65 | return doc.getSelection() + ''; 66 | } 67 | 68 | if (s === '') { 69 | s = getActiveSelection() || prompt(`Please enter the subject to look up in the ${languageNamesInEnglish.join('/')} Wikipedia:`); 70 | } else { 71 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 72 | } 73 | 74 | if (s) { 75 | /* Open the disambigation page for queries like `enw --dis Foo`. */ 76 | let matches = s.match(/^\s*--dis\s*(.+)/); 77 | if (matches) { 78 | s = matches[1] + disambigationPageSuffix; 79 | } 80 | 81 | /* The Wikipedia search works like "I'm feeling lucky" on most Wikipedia instances. If there is a complete match, it will redirect us there. */ 82 | location = `https://${languageCode}.wikipedia.org/w/index.php?searchToken=.&title=Special%3ASearch&ns0=1&search=${encodeURIComponent(s)}`; 83 | } 84 | })({ 85 | 86 | languageCode: 'sv', 87 | languageNamesInEnglish: ['Swedish'], 88 | disambigationPageSuffix: ' (olika betydelser)', 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /search/wikipedia/viw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Vietnamese Wikipedia. 3 | * 4 | * Use `--dis` as the first parameter to show the disambiguation page, if any. 5 | * 6 | * E.g. `enw --dis 1` would open `1 (disambiguation)`, which is a lot faster to 7 | * type then `enw 1 (disambiguation)`, and also works for the non-English 8 | * Wikipedia instances, which use other words for “disambiguation”. 9 | * 10 | * @title Vietnamese Wikipedia 11 | * @keyword viw 12 | */ 13 | (function (config) { 14 | const {languageCode, languageNamesInEnglish, disambigationPageSuffix} = config; 15 | /* Try to get the parameter string from the bookmarklet/search query. 16 | Fall back to the current text selection, if any. If those options 17 | both fail, prompt the user. 18 | */ 19 | var s = (function () { /*%s*/; }).toString() 20 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 21 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 22 | .replace(/\u0025s/, ''); 23 | 24 | /** 25 | * Get the active text selection, diving into frames and 26 | * text controls when necessary and possible. 27 | */ 28 | function getActiveSelection(doc) { 29 | if (arguments.length === 0) { 30 | doc = document; 31 | } 32 | 33 | if (!doc || typeof doc.getSelection !== 'function') { 34 | return ''; 35 | } 36 | 37 | if (!doc.activeElement) { 38 | return doc.getSelection() + ''; 39 | } 40 | 41 | var activeElement = doc.activeElement; 42 | 43 | /* Recurse for FRAMEs and IFRAMEs. */ 44 | try { 45 | if ( 46 | typeof activeElement.contentDocument === 'object' 47 | && activeElement.contentDocument !== null 48 | ) { 49 | return getActiveSelection(activeElement.contentDocument); 50 | } 51 | } catch (e) { 52 | return doc.getSelection() + ''; 53 | } 54 | 55 | /* Get the selection from inside a text control. */ 56 | if (typeof activeElement.value === 'string') { 57 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 58 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 59 | } 60 | 61 | return activeElement.value; 62 | } 63 | 64 | /* Get the normal selection. */ 65 | return doc.getSelection() + ''; 66 | } 67 | 68 | if (s === '') { 69 | s = getActiveSelection() || prompt(`Please enter the subject to look up in the ${languageNamesInEnglish.join('/')} Wikipedia:`); 70 | } else { 71 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 72 | } 73 | 74 | if (s) { 75 | /* Open the disambigation page for queries like `enw --dis Foo`. */ 76 | let matches = s.match(/^\s*--dis\s*(.+)/); 77 | if (matches) { 78 | s = matches[1] + disambigationPageSuffix; 79 | } 80 | 81 | /* The Wikipedia search works like "I'm feeling lucky" on most Wikipedia instances. If there is a complete match, it will redirect us there. */ 82 | location = `https://${languageCode}.wikipedia.org/w/index.php?searchToken=.&title=Special%3ASearch&ns0=1&search=${encodeURIComponent(s)}`; 83 | } 84 | })({ 85 | 86 | languageCode: 'vi', 87 | languageNamesInEnglish: ['Vietnamese'], 88 | disambigationPageSuffix: ' (định hướng)', 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /search/wikipedia/wikiv.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the English Wikivoyage. 3 | * 4 | * @title English Wikivoyage 5 | */ 6 | (function wikiv() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the subject to look up in the English Wikivoyage:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | /* The Wikivoyage search works like "I'm feeling lucky" on most Wikivoyage instances. If there is a complete match, it will redirect us there. */ 68 | location = 'https://en.wikivoyage.org/w/index.php?searchToken=&title=Special%3ASearch&search=' + encodeURIComponent(s); 69 | } 70 | })(); 71 | -------------------------------------------------------------------------------- /search/wikipedia/zhw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the specified or selected text in the Chinese Wikipedia. 3 | * 4 | * Use `--dis` as the first parameter to show the disambiguation page, if any. 5 | * 6 | * E.g. `enw --dis 1` would open `1 (disambiguation)`, which is a lot faster to 7 | * type then `enw 1 (disambiguation)`, and also works for the non-English 8 | * Wikipedia instances, which use other words for “disambiguation”. 9 | * 10 | * @title Chinese Wikipedia 11 | * @keyword zhw 12 | */ 13 | (function (config) { 14 | const {languageCode, languageNamesInEnglish, disambigationPageSuffix} = config; 15 | /* Try to get the parameter string from the bookmarklet/search query. 16 | Fall back to the current text selection, if any. If those options 17 | both fail, prompt the user. 18 | */ 19 | var s = (function () { /*%s*/; }).toString() 20 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 21 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 22 | .replace(/\u0025s/, ''); 23 | 24 | /** 25 | * Get the active text selection, diving into frames and 26 | * text controls when necessary and possible. 27 | */ 28 | function getActiveSelection(doc) { 29 | if (arguments.length === 0) { 30 | doc = document; 31 | } 32 | 33 | if (!doc || typeof doc.getSelection !== 'function') { 34 | return ''; 35 | } 36 | 37 | if (!doc.activeElement) { 38 | return doc.getSelection() + ''; 39 | } 40 | 41 | var activeElement = doc.activeElement; 42 | 43 | /* Recurse for FRAMEs and IFRAMEs. */ 44 | try { 45 | if ( 46 | typeof activeElement.contentDocument === 'object' 47 | && activeElement.contentDocument !== null 48 | ) { 49 | return getActiveSelection(activeElement.contentDocument); 50 | } 51 | } catch (e) { 52 | return doc.getSelection() + ''; 53 | } 54 | 55 | /* Get the selection from inside a text control. */ 56 | if (typeof activeElement.value === 'string') { 57 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 58 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 59 | } 60 | 61 | return activeElement.value; 62 | } 63 | 64 | /* Get the normal selection. */ 65 | return doc.getSelection() + ''; 66 | } 67 | 68 | if (s === '') { 69 | s = getActiveSelection() || prompt(`Please enter the subject to look up in the ${languageNamesInEnglish.join('/')} Wikipedia:`); 70 | } else { 71 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 72 | } 73 | 74 | if (s) { 75 | /* Open the disambigation page for queries like `enw --dis Foo`. */ 76 | let matches = s.match(/^\s*--dis\s*(.+)/); 77 | if (matches) { 78 | s = matches[1] + disambigationPageSuffix; 79 | } 80 | 81 | /* The Wikipedia search works like "I'm feeling lucky" on most Wikipedia instances. If there is a complete match, it will redirect us there. */ 82 | location = `https://${languageCode}.wikipedia.org/w/index.php?searchToken=.&title=Special%3ASearch&ns0=1&search=${encodeURIComponent(s)}`; 83 | } 84 | })({ 85 | 86 | languageCode: 'zh', 87 | languageNamesInEnglish: ['Chinese'], 88 | disambigationPageSuffix: ' (消歧义)', 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /search/yanimg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search Yandex with its reverse image search. 3 | * 4 | * @title Yandex.Images 5 | */ 6 | (function yanimg() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter your Yandex.Images search query or image URL:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | var words = s.split(' '), matches; 68 | 69 | /* If the first parameter looks like a URL, use Yandex’s reverse image search. */ 70 | if (words[0].match(/^(\w+:(\/\/)?)?[^\s]+(\.[^\s]+)+\//)) 71 | { 72 | location = 'https://yandex.com/images/search?rpt=imageview&url=' + encodeURIComponent(words[0]) + '&text=' + encodeURIComponent(words.slice(1).join(' ')); 73 | return; 74 | } 75 | 76 | location = 'https://yandex.com/images/search?text=' + encodeURIComponent(s); 77 | } 78 | })(); 79 | -------------------------------------------------------------------------------- /search/yttop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search YouTube for the specified or selected text inside “topic” videos, 3 | * using Invidious (“an alternative front-end to YouTube”). 4 | * 5 | * “Topic” videos are the official channels for artists/bands/musicians and can 6 | * be recognized by either “ - Topic” or “🎵” suffixed to the name. However, it 7 | * is easier to search for the text “Provided to YouTube by”, which is at the 8 | * start of the auto-generated description for music “videos”. 9 | * 10 | * @title YouTube topic search 11 | */ 12 | (function yttop() { 13 | /* Try to get the parameter string from the bookmarklet/search query. 14 | Fall back to the current text selection, if any. If those options 15 | both fail, prompt the user. 16 | */ 17 | var s = (function () { /*%s*/; }).toString() 18 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 19 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 20 | .replace(/\u0025s/, ''); 21 | 22 | /** 23 | * Get the active text selection, diving into frames and 24 | * text controls when necessary and possible. 25 | */ 26 | function getActiveSelection(doc) { 27 | if (arguments.length === 0) { 28 | doc = document; 29 | } 30 | 31 | if (!doc || typeof doc.getSelection !== 'function') { 32 | return ''; 33 | } 34 | 35 | if (!doc.activeElement) { 36 | return doc.getSelection() + ''; 37 | } 38 | 39 | var activeElement = doc.activeElement; 40 | 41 | /* Recurse for FRAMEs and IFRAMEs. */ 42 | try { 43 | if ( 44 | typeof activeElement.contentDocument === 'object' 45 | && activeElement.contentDocument !== null 46 | ) { 47 | return getActiveSelection(activeElement.contentDocument); 48 | } 49 | } catch (e) { 50 | return doc.getSelection() + ''; 51 | } 52 | 53 | /* Get the selection from inside a text control. */ 54 | if (typeof activeElement.value === 'string') { 55 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 56 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 57 | } 58 | 59 | return activeElement.value; 60 | } 61 | 62 | /* Get the normal selection. */ 63 | return doc.getSelection() + ''; 64 | } 65 | 66 | if (s === '') { 67 | s = getActiveSelection() || prompt('Please enter your YouTube topic search query:'); 68 | } else { 69 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 70 | } 71 | 72 | if (s) { 73 | s = s.replace(/\uFEFF/g, ''); 74 | 75 | location = 'https://yewtu.be/search?q=' + encodeURIComponent(s + ' "Provided to YouTube by"'); 76 | } 77 | })(); 78 | -------------------------------------------------------------------------------- /site-specific/README: -------------------------------------------------------------------------------- 1 | Site-specific 2 | 3 | Might as well turn these into Greasemonkey scripts one day. 4 | -------------------------------------------------------------------------------- /site-specific/discogs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search the Discogs master release for the given text. 3 | * 4 | * I prefer searching for the master record instead of a specific release, to 5 | * get more “general” info. 6 | * 7 | * @title Search Discogs 8 | */ 9 | (function discogs(document) { 10 | 'use strict'; 11 | 12 | /* Try to get the parameter string from the bookmarklet/search query. 13 | * Fall back to the current text selection, if any. If those options 14 | * both fail, prompt the user. */ 15 | let s = (function () { /*%s*/; }).toString() 16 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 17 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 18 | .replace(/\u0025s/, ''); 19 | 20 | /** 21 | * Get the active text selection, diving into frames and 22 | * text controls when necessary and possible. 23 | */ 24 | function getActiveSelection(document) { 25 | if (!document || typeof document.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!document.activeElement) { 30 | return document.getSelection() + ''; 31 | } 32 | 33 | const activeElement = document.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return document.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return document.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection(document) || prompt('Please enter the text to search in the Discogs release masters database:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection(document) + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://www.discogs.com/search/?type=master&limit=250&sort=year%2Casc&q=' + encodeURIComponent(s); 68 | } 69 | })(document); 70 | -------------------------------------------------------------------------------- /site-specific/emo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search Emojipedia for the given text or emoji. 3 | * 4 | * @title Emojipedia 5 | */ 6 | (function emo(document) { 7 | 'use strict'; 8 | 9 | /* Try to get the parameter string from the bookmarklet/search query. 10 | * Fall back to the current text selection, if any. If those options 11 | * both fail, prompt the user. */ 12 | let s = (function () { /*%s*/; }).toString() 13 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 14 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 15 | .replace(/\u0025s/, ''); 16 | 17 | /** 18 | * Get the active text selection, diving into frames and 19 | * text controls when necessary and possible. 20 | */ 21 | function getActiveSelection(document) { 22 | if (!document || typeof document.getSelection !== 'function') { 23 | return ''; 24 | } 25 | 26 | if (!document.activeElement) { 27 | return document.getSelection() + ''; 28 | } 29 | 30 | const activeElement = document.activeElement; 31 | 32 | /* Recurse for FRAMEs and IFRAMEs. */ 33 | try { 34 | if ( 35 | typeof activeElement.contentDocument === 'object' 36 | && activeElement.contentDocument !== null 37 | ) { 38 | return getActiveSelection(activeElement.contentDocument); 39 | } 40 | } catch (e) { 41 | return document.getSelection() + ''; 42 | } 43 | 44 | /* Get the selection from inside a text control. */ 45 | if (typeof activeElement.value === 'string') { 46 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 47 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 48 | } 49 | 50 | return activeElement.value; 51 | } 52 | 53 | /* Get the normal selection. */ 54 | return document.getSelection() + ''; 55 | } 56 | 57 | if (s === '') { 58 | s = getActiveSelection(document) || prompt('Please specify the text or emoji to search on Emojipedia.org:'); 59 | } else { 60 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection(document) + '$2'); 61 | } 62 | 63 | if (s) { 64 | location = 'https://emojipedia.org/search/?q=' + encodeURIComponent(s); 65 | } 66 | })(document); 67 | -------------------------------------------------------------------------------- /site-specific/githublet.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Put a bookmarklet's code on GitHub in a TEXTAREA so it is easy to copy. 3 | * 4 | * I use this because selecting and copying bookmarklet code to put them in my 5 | * Safari on iOS is a pain. 6 | * 7 | * @title GitHublet 8 | */ 9 | (function githublet() { 10 | var codeContainer = document.querySelector('.file .lines .highlight'); 11 | if (!codeContainer) { 12 | alert('No file contents found on page.'); 13 | return; 14 | } 15 | 16 | /* Extract the source code from the separately highlighted lines. */ 17 | var lineContainers = Array.prototype.slice.call(codeContainer.querySelectorAll('.line')); 18 | if (!lineContainers.length) { 19 | alert('No lines found in file contents.'); 20 | return; 21 | } 22 | var lines = []; 23 | lineContainers.forEach(function (line) { 24 | lines.push(line.textContent); 25 | }); 26 | 27 | /* Replace the code with a TEXTAREA of more or less the same dimensions. */ 28 | var textarea = document.createElementNS('http://www.w3.org/1999/xhtml', 'textarea'); 29 | textarea.value = 'javascript:' + lines.join('\n'); 30 | var codeContainerStyle = window.getComputedStyle(codeContainer); 31 | ['width', 'height', 'margin', 'padding'].forEach(function (property) { 32 | if (property === 'width' || property === 'height') { 33 | textarea.style[property] = codeContainerStyle[property]; 34 | } else { 35 | ['Top', 'Right', 'Bottom', 'Left'].forEach(function (edge) { 36 | textarea.style[property + edge] = codeContainerStyle[property + edge]; 37 | }); 38 | } 39 | }); 40 | textarea.style.border = 0; 41 | textarea.style.lineHeight = lineContainers[0].offsetHeight + 'px'; 42 | var lineContainerStyle = window.getComputedStyle(lineContainers[0]); 43 | ['fontFamily', 'fontSize', 'paddingLeft'].forEach(function (property) { 44 | textarea.style[property] = lineContainerStyle[property]; 45 | }); 46 | codeContainer.parentNode.replaceChild(textarea, codeContainer); 47 | textarea.focus(); 48 | textarea.select(); 49 | })(); 50 | -------------------------------------------------------------------------------- /site-specific/printriodos.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Improve the Triodos account statement print-out. 3 | * 4 | * This bookmarklets adds the account balance after each operation, which 5 | * makes it easier to keep track of the balance throughout the year/month. 6 | * 7 | * It also darkens the odd row background so it stands out more when printing 8 | * in black and white. (Remember to check the "Print background colours and 9 | * images" box.) 10 | * 11 | * @title Triodos pretty-printer 12 | */ 13 | (function printriodos() { 14 | var table = document.querySelector('table[id$=":searchResultTable"]'); 15 | 16 | /* Add a column with the total after each movement. */ 17 | var total = parseFloat( 18 | document.querySelector('.dataView table:first-child tr:first-child td:last-child') 19 | .textContent 20 | .replace('.', '') 21 | .replace(',', '.') 22 | ); 23 | 24 | Array.prototype.slice.call(table.querySelectorAll('tbody[id$="searchResultTable:tb"] td:last-child')).reverse().forEach(function (td) { 25 | td.style.textAlign = 'right'; 26 | 27 | var amount = td.textContent.replace('.', '').replace(',', '.'); 28 | total = (parseFloat(total) + parseFloat(amount)).toFixed(2); 29 | var newTd = td.cloneNode(); 30 | newTd.textContent = total.replace('.', ',').replace(/([0-9])([0-9]{3}),/, '$1.$2,'); 31 | td.classList.remove('lastItem'); 32 | td.parentNode.appendChild(newTd); 33 | }); 34 | 35 | /* Add a header cell for the column we just added. */ 36 | var th = table.querySelector('thead th:last-child'); 37 | var newTh = th.cloneNode(); 38 | newTh.textContent = 'Saldo (EUR)'; 39 | th.parentNode.appendChild(newTh); 40 | 41 | /* Keep the pretty background colours when printing. (This needs to be enabled in the printer options dialog, too.) */ 42 | Array.prototype.slice.call(document.querySelectorAll('link[rel="StyleSheet"][media="screen"]')).forEach(function (link) { 43 | link.media = 'all'; 44 | }); 45 | 46 | /* Avoid double borders on the last cell. */ 47 | document.head.appendChild(document.createElementNS('http://www.w3.org/1999/xhtml', 'style')).textContent = '.dataView th:last-child, .dataView td:last-child { border-right: 0; }'; 48 | 49 | /* Make the difference between rows more pronounced. */ 50 | document.head.appendChild(document.createElementNS('http://www.w3.org/1999/xhtml', 'style')).textContent = '.dataView tr.evenRow th, .dataView tr.evenRow td { background-color: #cee; }'; 51 | })(); 52 | -------------------------------------------------------------------------------- /site-specific/redtop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Show the top posts for the current/selected subreddit. 3 | * 4 | * I hardly ever use Reddit, but when I do, I’m most likely looking at a 5 | * humour-related subreddit to have a laugh, so “just play the hits, dammit!”. 6 | * 7 | * @title Subreddit’s top posts 8 | */ 9 | (function redtop() { 10 | 'use strict'; 11 | 12 | /* Try to get the parameter string from the bookmarklet/search query. 13 | * Fall back to the current text selection, if any. If those options 14 | * both fail, prompt the user. */ 15 | let s = (function () { /*%s*/; }).toString() 16 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 17 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 18 | .replace(/\u0025s/, ''); 19 | 20 | /** 21 | * Get the active text selection, diving into frames and 22 | * text controls when necessary and possible. 23 | */ 24 | function getActiveSelection(document) { 25 | if (!document || typeof document.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!document.activeElement) { 30 | return document.getSelection() + ''; 31 | } 32 | 33 | const activeElement = document.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return document.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return document.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection(document); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection(document) + '$2'); 64 | } 65 | 66 | const subredditRegexp = /\b(?<redditUrlPrefix>https?:\/\/([^\/]+\.)?reddit\.com\/)?(?<subredditName>r\/[a-zA-Z0-9_-]+)/; 67 | 68 | /* Check the bookmarklet parameter or the selected text. */ 69 | let subredditMatches = s.match(subredditRegexp); 70 | 71 | /* Check the current URL. */ 72 | if (!subredditMatches) { 73 | subredditMatches = location.href.match(subredditRegexp); 74 | } 75 | 76 | /* Prompt for the subreddit name or URL. */ 77 | if (!subredditMatches) { 78 | s = prompt('Please enter the `r/SubredditNameHere` or the full `https://www.reddit.com/r/SubredditNameHere` URL to view its top posts.', location); 79 | subredditMatches = s.match(subredditRegexp); 80 | } 81 | 82 | if (!subredditMatches) { 83 | alert(`“${s}” does not look like a valid subreddit.`); 84 | return; 85 | } 86 | 87 | location = (subredditMatches.groups.redditUrlPrefix || 'https://www.reddit.com/') 88 | + subredditMatches?.groups.subredditName 89 | + '/top/?sort=top&t=all'; 90 | })(); 91 | -------------------------------------------------------------------------------- /site-specific/somanp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Show the currently playing track in the window/tab title on SomaFM Player 3 | * pages like https://somafm.com/player/#/now-playing/cliqhop 4 | * 5 | * @title SomaFM np 6 | */ 7 | (function somanp() { 8 | var playlistContainer = document.querySelector('.now-playing .list-body'); 9 | if (!playlistContainer) { 10 | return; 11 | } 12 | 13 | var originalTitle = document.title; 14 | 15 | function updateTitle() { 16 | var artistNode = playlistContainer.querySelector('.row > :nth-child(2)'); 17 | var titleNode = playlistContainer.querySelector('.row > :nth-child(3)'); 18 | 19 | if (!artistNode || !titleNode) { 20 | return; 21 | } 22 | 23 | document.title = artistNode.textContent 24 | + ' - ' + titleNode.textContent.toLowerCase() 25 | + ' | ' + originalTitle; 26 | } 27 | 28 | var observer = new MutationObserver(updateTitle); 29 | 30 | observer.observe(playlistContainer, { 31 | childList: true, 32 | subtree: true 33 | }); 34 | 35 | updateTitle(); 36 | })(); 37 | -------------------------------------------------------------------------------- /site-specific/strava/README: -------------------------------------------------------------------------------- 1 | Strava 2 | 3 | Utilities for “the social network for athletes”. 4 | -------------------------------------------------------------------------------- /site-specific/strava/pace2speed.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Convert pace (e.g. “4:38/km”) to speed (e.g. “12.95 km/h”) on a web page. 3 | * 4 | * @title Pace2Speed 5 | */ 6 | (function pace2speed() { 7 | 'use strict'; 8 | 9 | /* Recursively execute the logic on the document and its sub-documents. */ 10 | function execute(document) { 11 | const possiblePaceLeafNodes = []; 12 | 13 | Array.from(document.querySelectorAll('body *')).forEach(node => { 14 | const matches = node.textContent.match(/^\s*(?<minutes>\d+)\s*(:|'|′|min(ute)?s?)\s*(?<seconds>\d+)\s*(''|"|″|sec(ond)?s?)?\s*\/\s*(?<unit>km|mi)/) 15 | ?? node.textContent.match(/^\s*(?<minutes>\d+)\s*:\s*(?<seconds>\d+)\s*(?:min(?:utes?)?)\s*\/\s*(?<unit>km|mi)/); 16 | 17 | if (!matches) { 18 | return; 19 | } 20 | 21 | const durationInSeconds = parseInt(matches.groups.minutes, 10) * 60 + parseInt(matches.groups.seconds, 10); 22 | let speedInKilometersPerHour = 3600 / durationInSeconds; 23 | if (matches.groups.unit === 'mi') { 24 | speedInKilometersPerHour *= 1.60934; 25 | } 26 | speedInKilometersPerHour = speedInKilometersPerHour.toFixed(1); 27 | 28 | node.setAttribute('xxxJanPace2Speed', speedInKilometersPerHour); 29 | possiblePaceLeafNodes.push(node); 30 | }); 31 | 32 | /* Replace the text content of the deepest nodes. */ 33 | possiblePaceLeafNodes.forEach(node => { 34 | if (node.querySelector('[xxxJanPace2Speed]')) { 35 | return; 36 | } 37 | 38 | node.textContent = `${node.getAttribute('xxxJanPace2Speed')} km/h`; 39 | }); 40 | 41 | /* Recurse for frames and IFRAMEs. */ 42 | try { 43 | Array.from( 44 | document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]') 45 | ).forEach( 46 | elem => execute(elem.contentDocument) 47 | ); 48 | } catch (e) { 49 | /* Catch and ignore exceptions for out-of-domain access. */ 50 | } 51 | } 52 | 53 | execute(document); 54 | })(); 55 | -------------------------------------------------------------------------------- /site-specific/strava/sact.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search for a Strava activity. 3 | * 4 | * @title Strava activity 5 | */ 6 | (function sact() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the Strava activity name to look for:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://www.strava.com/athlete/training?keywords=' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /site-specific/strava/sath.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search for a Strava member (athlete). 3 | * 4 | * @title Strava athlete 5 | */ 6 | (function sath() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the Strava athlete/member name to look for:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://www.strava.com/athletes/search?utf8=%E2%9C%93&text=' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /site-specific/strava/sclub.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search for a Strava club. 3 | * 4 | * @title Strava club 5 | */ 6 | (function sclub() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the Strava club name to look for:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://www.strava.com/clubs/search?utf8=%E2%9C%93&text=' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /site-specific/strava/sseg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search for a Strava segment. 3 | * 4 | * @title Strava segment 5 | */ 6 | (function sseg() { 7 | /* Try to get the parameter string from the bookmarklet/search query. 8 | Fall back to the current text selection, if any. If those options 9 | both fail, prompt the user. 10 | */ 11 | var s = (function () { /*%s*/; }).toString() 12 | .replace(/^function\s*\(\s*\)\s*\{\s*\/\*/, '') 13 | .replace(/\*\/\s*\;?\s*\}\s*$/, '') 14 | .replace(/\u0025s/, ''); 15 | 16 | /** 17 | * Get the active text selection, diving into frames and 18 | * text controls when necessary and possible. 19 | */ 20 | function getActiveSelection(doc) { 21 | if (arguments.length === 0) { 22 | doc = document; 23 | } 24 | 25 | if (!doc || typeof doc.getSelection !== 'function') { 26 | return ''; 27 | } 28 | 29 | if (!doc.activeElement) { 30 | return doc.getSelection() + ''; 31 | } 32 | 33 | var activeElement = doc.activeElement; 34 | 35 | /* Recurse for FRAMEs and IFRAMEs. */ 36 | try { 37 | if ( 38 | typeof activeElement.contentDocument === 'object' 39 | && activeElement.contentDocument !== null 40 | ) { 41 | return getActiveSelection(activeElement.contentDocument); 42 | } 43 | } catch (e) { 44 | return doc.getSelection() + ''; 45 | } 46 | 47 | /* Get the selection from inside a text control. */ 48 | if (typeof activeElement.value === 'string') { 49 | if (activeElement.selectionStart !== activeElement.selectionEnd) { 50 | return activeElement.value.substring(activeElement.selectionStart, activeElement.selectionEnd); 51 | } 52 | 53 | return activeElement.value; 54 | } 55 | 56 | /* Get the normal selection. */ 57 | return doc.getSelection() + ''; 58 | } 59 | 60 | if (s === '') { 61 | s = getActiveSelection() || prompt('Please enter the Strava segment title to look for:'); 62 | } else { 63 | s = s.replace(/(^|\s|")~("|\s|$)/g, '$1' + getActiveSelection() + '$2'); 64 | } 65 | 66 | if (s) { 67 | location = 'https://www.strava.com/segments/search?utf8=%E2%9C%93&filter_type=cycling&min-cat=0&max-cat=5&terrain=all&keywords=' + encodeURIComponent(s); 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /tests/README: -------------------------------------------------------------------------------- 1 | A collection of saved web pages to test my bookmarklets. 2 | --------------------------------------------------------------------------------