Repository: github.com/mafredri/safari-bookmarklets
Simply drag the link (bookmarklet) you want to you bookmarks (Favorites for easy access).
On iOS devices the installation process is more cumbersome, but possible. See below.
javascript:(function(){
/**
* DuckStartGo
*
* Toggle between DuckDuckGo. Starpage.com and Google, maintaining the search
* query. From DuckDuckGo to Startpage.com, from Startpage.com to Google and
* from Google to DuckDuckGo. If none of these sites are active, search for the
* highlighted text via DuckDuckGo. If no text is highlighted, simply navigate
* to DuckDuckGo.
*
* Author: Mathias Fredriksson
* Version: 0.0.3
*/
function run(){switch(window.location.hostname.replace('www.','')){case'duckduckgo.com':
return toStartpage(document.querySelector('[name=q]').value,!1);case'startpage.com':
return toGoogle(document.querySelector('#q').value);case'google.com':
return toDuckDuckGo(document.querySelector('[name=q]').value,!1);default:let t=document.getSelection().toString()
;if(''!==t)return toDuckDuckGo(t,!1)}window.location='https://www.duckduckgo.com'}function toStartpage(t,o){
if(o)return navigateViaForm('POST','https://www.startpage.com/sp/search',{q:t});t=encodeURIComponent(t),
document.location=`https://www.startpage.com/sp/search?q=${t}`}function toDuckDuckGo(t,o){
if(o)return navigateViaForm('POST','https://www.duckduckgo.com/',{q:t});t=encodeURIComponent(t),
document.location=`https://www.duckduckgo.com/?q=${t}`}function toGoogle(t){t=encodeURIComponent(t),
document.location=`https://www.google.com/search?q=${t}`}function navigateViaForm(t,o,e){
const c=document.createElement('form');c.style.visibility='hidden',c.method=t,c.action=o;for(const t of Object.keys(e)){
const o=document.createElement('input');o.name=t,o.value=e[t],c.appendChild(o)}document.body.appendChild(c),c.submit()}
run();})();void(0);
javascript:(function(){
/**
* GoDoc
*
* Navigates to GoDoc from the current repository, e.g. on GitHub.
*
* Author: Mathias Fredriksson
* Version: 0.0.0
*/
const re=/\/((tree|blob|commits?|issues|pull|graphs|settings|network)\/[^/]+|(issues|pulls|projects|wiki|pulse|community|network|settings)$)/,loc=window.location
;window.location='https://godoc.org/'+loc.hostname+loc.pathname.replace(re,'');})();void(0);
javascript:(function(){
/**
* Google Translate: Selection
*
* Translate only the selected text using automatic language detection.
* Note: This is still a work-in-progress (WIP).
*
* Author: Mathias Fredriksson
* Version: 0.0.1
*/
async function main(){
const e=document.getSelection(),t=e.getRangeAt(0),n=document.createTreeWalker(t.commonAncestorContainer,NodeFilter.SHOW_ALL)
;let o=[];for(;n.nextNode();){const t=n.currentNode;e.containsNode(t,!1)&&(o=[...o,t])}
const a=o.filter(e=>e.nodeType===Node.TEXT_NODE&&''!==e.nodeValue.trim()),s=a.map((e,t)=>`${e.nodeValue.trim()}`).join('\n'),[r]=await translate(s)
;a.forEach((e,t)=>{e.nodeValue=r[t][0]})}function translate(e,t='en',n='auto'){
const o=`https://translate.googleapis.com/translate_a/single?client=gtx&sl=${n}&tl=${t}&dt=t&q=${e=encodeURI(e.replace(/\ /g,'+'))}`
;return new Promise((e,t)=>{const n=new XMLHttpRequest;n.onreadystatechange=function(){if(4===n.readyState){
if(200!==n.status)return void t(n.status);const o=JSON.parse(n.responseText);e(o)}},n.open('GET',o,!0),n.send(null)})}
main();})();void(0);
javascript:(function(){
/**
* Google Translate: Toolbar
*
* Inject a Google Translate toolbar into the current page, allows
* toggling translate on/off but might disrupt the page layout and
* doesn't translate as well as the "Google Translate" bookmarklet.
*
* Author: Mathias Fredriksson
* Version: 0.0.1
*/
const target=document.createElement('div');target.id='googleTranslateElement',document.body.prepend(target)
;const inject=document.createElement('script');function googleTranslateInit(){
const e=new google.translate.TranslateElement({pageLanguage:''},'googleTranslateElement');console.log(e)}
inject.type='text/javascript',
inject.src=window.location.protocol+'//translate.google.com/translate_a/element.js?cb=googleTranslateInit',
window.googleTranslateInit=googleTranslateInit,document.head.append(inject);})();void(0);
javascript:(function(){
/**
* Google Translate
*
* Translates the current page using automatic language detection. When
* clicked on a translated page, will return to the original.
*
* Author: Mathias Fredriksson
* Version: 0.0.1
*/
function translate(){
const t='https://translate.google.com/translate?sl=auto&tl=en&js=y&prev=_t&hl=en&ie=UTF-8&edit-text=&u='+encodeURIComponent(window.location.href)
;window.location=t}function untranslate(){
let t=window.location.search.split('&').filter(t=>t.startsWith('u='))[0].slice(2);t=decodeURIComponent(t),
window.location=t}function untranslateDomain(){
const t=window.location.href.includes('_x_tr_sch=http'),o=window.location.origin.replace('--','[dash]').replace('-','.').replace('[dash]','-').replace('.translate.goog','')
;let n=window.location.search.slice(1).split('&').filter(t=>!t.startsWith('_x_tr_')).join('&');n=n.length?`?${n}`:''
;let e=window.location.href.replace(window.location.origin,o).replace(window.location.search,n)
;t&&(e=e.replace('https://','http://')),window.location=e}
['://translate.google.com','://translate.googleusercontent.com'].some(t=>window.location.origin.includes(t))?untranslate():window.location.origin.endsWith('.translate.goog')?untranslateDomain():translate();})();void(0);
javascript:(function(){
/**
* Add to Pinboard
*
* Open a popup to bookmark the current page in Pinboard, information is
* extracted from meta-tags where possible and some smarts are used for
* constructing the title and description. If the document defines
* keywords, tags will be pre-populated as well.
*
* Note: Wayback Machine proxies Window.open via Wombat.js which causes
* a nested redirect and would break this bookmarklet. As a workaround,
* the window.location is changed instead of opening a new window.
*
* Author: Mathias Fredriksson
* Version: 0.0.3
*/
const url=firstNonEmpty(location.href.startsWith('https://web.archive.org/web/')?location.href:'',metaP('og:url'),metaP('al:web:url'),location.href)
;let title=firstNonEmpty(metaID('title'),metaP('title'),metaP('og:title'),metaP('twitter:title'),query('h1','innerText'),query('title','innerText')),description=firstNonEmpty(metaID('description'),metaP('description'),metaP('og:description'),metaP('twitter:description'),metaN('description'))
;description&&(description=`Description: ${quote(description=description.replace(/\ Contribute to [^\ ]* development by creating an account on GitHub.$/,''))}`)
;let tags=[],keywords=firstNonEmpty(metaN('keywords'))
;keywords&&(tags=keywords.split(',').map(t=>t.trim()).filter(t=>''!==t))
;const author=firstNonEmpty(metaID('author'),metaN('author')),siteName=firstNonEmpty(metaP('og:site_name'))
;author&&!title.includes(author)?title+=` | ${author}`:siteName&&!title.includes(siteName)&&(title+=` | ${siteName}`)
;let sel=document.getSelection().toString();''!==sel&&(sel=`Excerpt: ${quote(sel)}`),
description&&sel?description=`${description}\n${sel}`:sel&&(description=sel)
;const queryParams=[['url',url],['title',title],['description',description],['tags',tags.join(',')]].map(t=>`${t[0]}=${encodeURIComponent(t[1])}`).join('&'),openURL=`https://pinboard.in/add?showtags=yes&${queryParams}`
;function firstNonEmpty(...t){const[e]=t.map(t=>t.trim()).filter(t=>''!==t);return e||''}function query(t,e){
const o=document.querySelector(t);return o?o[e]:''}function metaID(t){return query(`meta#${t}`,'content')}
function metaN(t){return query(`meta[name="${t}"]`,'content')}function metaP(t){
let e=query(`meta[property="${t}"]`,'content');return e||(e=query(`meta[itemprop="${t}"]`,'content')),e}
function quote(t){return`<blockquote>${t.trim()}</blockquote>`}
url.startsWith('https://web.archive.org/web/')?window.location=openURL:open(openURL,'Pinboard','toolbar=no,scrollbars=yes,width=750,height=700');})();void(0);
javascript:(function(){
/**
* Spotify to Apple Music
*
* Searches Apple Music for the song or album being viewed on Spotify.
*
* Author: Mathias Fredriksson
* Version: 0.0.0
*/
async function main(){
const[t,e,n]=['twitter:title','twitter:audio:artist_name','og:type'].map(t=>document.querySelector(`meta[property="${t}"]`).content)
;'music.song'===n?entity='song':'music.album'===n?entity='album':alert(`Unknown type: ${n}`)
;let r=`${t} ${e}`,a=await search(r);a||(r=r.replace(/\([^)]+\)/g,''),a=await search(r)),render(a)}function search(t){
const e=`https://itunes.apple.com/search?term=${t=encodeURI(t.replace(/\ /g,'+'))}&media=music&entity=${entity}`
;return new Promise((t,n)=>{const r=new XMLHttpRequest;r.onreadystatechange=function(){if(4===r.readyState){
if(200!==r.status)return void n(r.status);const e=JSON.parse(r.responseText);e.resultCount||t(null),t(e.results)}},
r.open('GET',e,!0),r.send(null)})}function render(t){let e=[]
;for(let n of t)e=[...e,`<p><a href="${n.collectionViewUrl}"><img src="${n.artworkUrl100}">${n.collectionName} - ${n.artistName}</a></p>`]
;document.write(e.join(''))}main();})();void(0);
javascript:(function(){
/**
* Tori: Relinkify
*
* Relinkify sold links on Tori.fi.
*
* Author: Mathias Fredriksson
* Version: 0.0.0
*/
for(const e of Array.from(document.querySelectorAll('.adw_desc.gray_text'))){
const t=e.parentNode,o=e.childNodes[0],r=t.id.replace(/^item_/,''),a=o.textContent.replace(/\ /g,'_').replace(/\+/g,'_').replace(/[ÄÅ]/g,'A').replace(/[åä]/g,'a').replace('Ö','O').replace('ö','o').trim(),c=t.querySelectorAll('.cat_geo a')[0].textContent.trim().toLowerCase()
;console.log(r,a,c);const l=document.createElement('a');l.classList.add('item_link','nohistory'),
l.href=`https://www.tori.fi/${c}/${a}_${r}.htm?aw=1`,l.textContent=o.textContent,e.insertBefore(l,o),o.remove()}})();void(0);
javascript:(function(){
/**
* Wayback Machine
*
* Opens the latest snapshot of the current URL from Internet Archive.
* Clicking the bookmarklet while viewing an archive redirects to the
* live page.
*
* Author: Mathias Fredriksson
* Version: 0.0.0
*/
let loc=window.location.href;if('safari-resource:/ErrorPage.html'===loc){
const o=new RegExp('['+String.fromCharCode(8220)+String.fromCharCode(8221)+']','g')
;loc=(loc=document.querySelector('.error-message').textContent.split(o)[1]).replace(String.fromCharCode(8206),'')}
function showArchive(o){o=encodeURI(o),window.location=`http://web.archive.org/web/submit?type=replay&url=${o}`}
function showLive(o){(o=o.split('/').slice(5).join('/')).startsWith('http')||(o='http://'+o),window.location=o}
loc.includes('://web.archive.org/web/')?showLive(loc):showArchive(loc);})();void(0);
javascript:(function(){
/**
* YouTube PiP
*
* Enable YouTube PiP in macOS and on iOS devices. Additionally launches a
* watchdog that keeps PiP
*
* Credits for the original code go to Code Everywhere, from:
* https://codeeverywhere.ca/post.php?id=56#Enable-YouTube-picture-in-picture-on-iOS-14-Safari-with-this-Siri-Shortcut
*
* Author: Mathias Fredriksson
* Version: 0.0.2
*/
function enablePiP(e){let t=document.querySelector('.ytp-ad-skip-button-text');if(t)return t.click(),
void setTimeout(()=>enablePiP(e),250);let i=document.querySelector('video');if(i.setAttribute('controls',!0),
i!==window._pip_watchdog.video){const e=e=>setTimeout(()=>{window._pip_watchdog.video.setAttribute('controls',!0)},250)
;window._pip_watchdog.video=i,i.addEventListener('webkitpresentationmodechanged',t=>(e(t),t.stopPropagation()),!0),
i.addEventListener('play',e),i.addEventListener('pause',e)}e&&setTimeout(()=>{
window._pip_watchdog.video.webkitSetPresentationMode('picture-in-picture')},250)}function run(){
if(window._pip_watchdog)return window._pip_watchdog.url=document.location.href,void enablePiP(!0);window._pip_watchdog={
url:document.location.href,video:null},enablePiP(!0),setInterval(()=>{
window._pip_watchdog.url!=document.location.href&&enablePiP(!1)},500)}run();})();void(0);
Installation instructions for iOS devices: