-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbookmarklets.html
More file actions
314 lines (284 loc) · 16 KB
/
bookmarklets.html
File metadata and controls
314 lines (284 loc) · 16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
<!DOCTYPE html>
<html lang="en-US">
<!-- Head -->
<head>
<!-- Meta Tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- File Links -->
<link rel="shortcut icon" href="/assets/icon.ico" type="image/x-icon">
<link rel="stylesheet" href="/assets/style.css">
<link rel="alternate" type="application/rss+xml" title="Zachary Kai" href="/assets/rss.xml">
<link rel="webmention" href="https://webmention.io/zacharykai.net/webmention" />
<link rel="canonical" href="https://zacharykai.net/bookmarklets">
<!-- Page Info -->
<title>Bookmarklets | Zachary Kai</title>
<meta name="date" content="2025-03-14">
<meta name="last-modified" content="2025-03-16">
<meta name="description" content="Herein you'll find my bookmarklets collection: snippets of JavaScript saved as bookmarks in my browser. Perhaps you'll find them useful too!">
</head>
<!-- Body -->
<body>
<!-- Skip Link -->
<p><a href="#top" class="essentials">Begin reading...</a></p>
<!-- Site Header -->
<header><nav><a href="/">Zachary Kai</a></nav></header>
<!-- Main Content -->
<main class="h-entry e-content">
<!-- Page Header -->
<header>
<!-- Breadcrumbs -->
<p class="essentials"><a href="/">Homepage</a> • <a href="/sitemap#pages">Pages</a></p>
<!-- Page Title -->
<h1 class="p-name">Bookmarklets</h1>
<!-- Necessary Metadata -->
<p class="essentials">
<strong>Written By</strong>: <a href="/about">Zachary Kai</a> »
<strong>Published</strong>: <time class="dt-published" datetime="2025-03-14">14 Mar 2025</time> |
<strong>Updated</strong>: <time class="dt-modified" datetime="2025-03-16">16 Mar 2025</time>
</p>
<!-- Other Metadata -->
<details class="essentials">
<summary>Expand For Other (Hopefully Useful) Metadata</summary>
<ul>
<li>Reading Time: ~3 min (at 238 WPM)
<li>Word Count: 637</li>
</ul>
</details>
</header>
<!-- Page Body -->
<section class="e-content">
<!-- Introduction -->
<p id="top" class="p-summary">Herein you'll find my bookmarklets collection: snippets of JavaScript saved as bookmarks in my browser. Perhaps you'll find them useful too!</p>
<p><strong>Table Of Contents</strong></p>
<ul>
<li><a href="#color-inverter">Color Inverter</a></li>
<li><a href="#internet-archiver">Internet Archiver</a></li>
<li><a href="#rss-finder">RSS Feed Finder</a></li>
<li><a href="#webmention-checker">Webmention Checker</a></li>
<li><a href="#word-counter">Word Counter</a></li>
</ul>
<h2 id="color-inverter">Color Inverter</h2>
<p>This 'forces' dark mode or light mode on a page.</p>
<pre><code>javascript:(function(){const style=document.createElement('style');style.id='invert-colors-style';if(document.getElementById('invert-colors-style')){document.getElementById('invert-colors-style').remove()}else{style.textContent='html{filter:invert(100%)hue-rotate(180deg)}img,video,canvas{filter:invert(100%)hue-rotate(180deg)}';document.head.appendChild(style)}})();</code></pre>
<details>
<summary>Expanded Code</summary>
<pre><code>(function() {
// Create a new style element
const style = document.createElement('style');
// Give it an ID
style.id = 'invert-colors-style';
// Check if the style element exists
if (document.getElementById('invert-colors-style')) {
// If it exists, toggles effect off
document.getElementById('invert-colors-style').remove();
} else {
// If it doesn't, add CSS rules for inverting colors
style.textContent = `
html {
filter: invert(100%) hue-rotate(180deg);
}
// Apply inverse filter to media elements to revert to normal colors
img, video, canvas {
filter: invert(100%) hue-rotate(180deg);
}
`;
// Add style element to the page
document.head.appendChild(style);
}
})();
```</pre>
</details>
<hr>
<h2 id="internet-archiver">Internet Archiver</h2>
<p>Upon click, this'll open a new tab which sends the previous tab to the Internet Archive for saving. When the page loads (it takes some time) it's finished.</p>
<pre><code>javascript:(function(){var url=encodeURIComponent(window.location.href);window.open('https://web.archive.org/save/'+url,'_blank');})();</code></pre>
<details>
<summary>Expanded Code</summary>
<pre><code>(function() {
// Get the current URL
var url = encodeURIComponent(window.location.href);
// Open the Wayback Machine's save page with the current URL
window.open('https://web.archive.org/save/' + url, '_blank');
})();
```</pre>
</details>
<hr>
<h2 id="rss-finder">RSS Feed Finder</h2>
<p>This'll find RSS feeds for the site you're visiting.</p>
<pre><code>javascript:(function(){var feeds=[];var links=document.getElementsByTagName('link');for(var i=0;i<links.length;i++){var link="links[i];var" type="link.getAttribute('type');var" rel="link.getAttribute('rel');var" href="link.getAttribute('href');if(href&&(type==='application/rss+xml'||type==='application/atom+xml'||(rel==='alternate'&&(type==='application/rss+xml'||type==='application/atom+xml')))){if(href.indexOf('http')!==0){if(href.startsWith('/')){href=window.location.origin+href;}else{href=window.location.href.substring(0,window.location.href.lastIndexOf('/')+1)+href;}}feeds.push(href);}}if(feeds.length>0){window.open(feeds[0],'_blank');}else{alert('No RSS feeds found on this page.');}})();</code></pre>
<details>
<summary>Expanded Code</summary>
<pre><code>javascript:(function(){
var feeds = [];
var links = document.getElementsByTagName('link');
for (var i = 0; i < links.length; i++) {
var link = links[i];
var type = link.getAttribute('type');
var rel = link.getAttribute('rel');
var href = link.getAttribute('href');
if (href && (type === 'application/rss+xml' || type === 'application/atom+xml' || (rel === 'alternate' && (type === 'application/rss+xml' || type === 'application/atom+xml')))) {
if (href.indexOf('http') !== 0) {
if (href.startsWith('/')) {
href = window.location.origin + href;
} else {
href = window.location.href.substring(0, window.location.href.lastIndexOf('/') + 1) + href;
}
}
feeds.push(href);
}
}
if (feeds.length > 0) {
// Open the first RSS feed in the browser's default RSS viewer
window.open(feeds[0], '_blank');
} else {
// No RSS feed found, alert the user
alert('No RSS feeds found on this page.');
}
})();
```</pre>
</details>
<hr>
<h2 id="webmention-checker">Webmention Checker</h2>
<p>This checks for a site's webmention endpoint.</p>
<pre><code>javascript:(function(){function findWebmentionsEndpoint(){const e=document.querySelectorAll('link[rel="webmention"]');if(e.length>0)return e[0].getAttribute('href');const t=document.querySelectorAll('a[rel="webmention"]');if(t.length>0)return t[0].getAttribute('href');const n=document.querySelector('meta[http-equiv="Link"]');if(n){const o=n.getAttribute('content'),r=o.match(/<([^>]+)>;\s*rel=(?:%22|%27)webmention(?:%22|%27)/);if(r)return r[1]}return null}alert(findWebmentionsEndpoint()||'No webmentions endpoint found');})();</code></pre>
<details>
<summary>Expanded Code</summary>
<pre><code>(function() {
function findWebmentionsEndpoint() {
const linkTags = document.querySelectorAll('link[rel="webmention"]');
if (linkTags.length > 0) {
return linkTags[0].getAttribute('href');
}
const headerLinks = document.querySelectorAll('a[rel="webmention"]');
if (headerLinks.length > 0) {
return headerLinks[0].getAttribute('href');
}
const headers = document.querySelector('meta[http-equiv="Link"]');
if (headers) {
const content = headers.getAttribute('content');
const match = content.match(/<([^>]+)>;\s*rel=(?:%22|%27)webmention(?:%22|%27)/);
if (match) {
return match[1];
}
}
return null;
}
const result = findWebmentionsEndpoint();
alert(result || 'No webmentions endpoint found');
})();
```</pre>
</details>
<hr>
<h2 id="word-counter">Word Counter</h2>
<p>This counts how many words within the <code>main</code> HTML element and the estimated reading time.</p>
<pre>
<code>
javascript:(function(){const main=document.querySelector('main');if(!main){alert('No main tag found on this page!');return;}const text=main.textContent.trim();const words=text.split(/\s+/).filter(word=>word.length>0);const wordCount=words.length;const avgReadingSpeed=200;const readingTimeMinutes=wordCount/avgReadingSpeed;const mins=Math.floor(readingTimeMinutes);const secs=Math.floor((readingTimeMinutes-mins)*60);const readingTime=`${mins}:${secs.toString().padStart(2,'0')}`;alert(`The main tag contains ${wordCount} words.\nEstimated reading time: ${readingTime} mins`);})();
</code>
</pre>
<details>
<summary>Expanded Code</summary>
<pre>
<code>
javascript:(function() {
// Find the main content area of the page
const main = document.querySelector('main');
// Check if the main tag exists
if (!main) {
alert('No main tag found on this page!');
return;
}
// Extract text content and trim whitespace
const text = main.textContent.trim();
// Split text into words and filter out empty strings
const words = text.split(/\s+/).filter(word => word.length > 0);
// Count the number of words
const wordCount = words.length;
// Define average reading speed (words per minute)
const avgReadingSpeed = 200;
// Calculate reading time in minutes
const readingTimeMinutes = wordCount / avgReadingSpeed;
// Convert to minutes and seconds
const mins = Math.floor(readingTimeMinutes);
const secs = Math.floor((readingTimeMinutes - mins) * 60);
// Format reading time as mm:ss
const readingTime = `${mins}:${secs.toString().padStart(2, '0')}`;
// Display the result
alert(`The main tag contains ${wordCount} words.
Estimated reading time: ${readingTime} mins`);
})();
</code>
</pre>
</details>
<!-- Closing -->
<p>•--♡--•</p>
</section>
<section>
<!-- Closing Metadata -->
<section>
<!-- Tags -->
<p class="essentials"><strong>Tags</strong>: <a href="/tags?tag=resources">resources</a> · <a href="/tags?tag=indieweb">indieweb</a></p>
</section>
<section>
<!-- Copy + Share -->
<p><strong>Copy + Share</strong>: <a href="/bookmarklets">zacharykai.net/bookmarklets</a></p>
</section>
</section>
</main>
<!-- Back To Top Link -->
<p><a href="#top" class="essentials">Read again...</a></p>
<!-- H-Card -->
<section class="h-card vcard">
<section id="h-card-image">
<picture>
<source srcset="/assets/icon.webp" type="image/webp">
<img class="u-photo" loading="lazy" src="/assets/icon.png" alt="Zachary Kai's digital drawing: 5 stacked books (blue/teal/green/purple, black spine designs), green plant behind top book, purple heart on either side.">
</picture>
</section>
<section id="h-card-content">
<p><strong><a class="u-url u-id p-name" href="https://zacharykai.net" rel="me"><span class="fn">Zachary Kai</span></a></strong> — <span class="p-pronouns">he/him</span> | <a class="u-email email" href="mailto:hi@zacharykai.net" rel="me">hi@zacharykai.net</a></p>
<p class="p-note">Zachary Kai is a space fantasy writer, offbeat queer, traveler, zinester, and avowed generalist. The internet is his livelihood and lifeline.</p>
</section>
</section>
<!-- Footer -->
<footer>
<!-- Acknowledgement Of Country -->
<p style="margin-top: 0.25em;"><strong>Acknowledgement Of Country</strong>: I owe my existence to the <a href="https://kht.org.au/" rel="noopener">Koori people's</a> lands: tended for millennia by the traditional owners and storytellers. What a legacy. May it prevail.</p>
<!-- Reply Via -->
<p>
<strong>Reply Via</strong>:
<a href="/contact">Email</a> |
<a href="/guestbook">Guestbook</a> |
<a href="/unoffice-hours">UnOffice Hours</a> |
<a href="/webmention" rel="noopener">Webmention</a>
</p>
<!-- Footer Menu -->
<p>
<strong>Est. 2024</strong> ||
<a href="/about">About</a> |
<a href="/art">Art</a> |
<a href="/colophon">Accessibility & Colophon</a> |
<a href="/changelog">Changelog</a> |
<a href="/jots">Jots</a> |
<a href="/now">Now</a> |
<a href="/random">Random</a> |
<a href="/assets/rss.xml">RSS</a> |
<a href="/sitemap">Sitemap</a> |
<a href="/uses">Uses</a>
</p>
<!-- Elsewhere Links -->
<p style="margin-bottom: 0;">
<strong>Elsewhere</strong>:
<a href="https://roadlessread.com" rel="noopener">Blog</a> |
<a href="https://lunaseeker.com/catalog/" rel="noopener">Books</a> |
<a href="https://lunaseeker.com/cv" rel="noopener">CV</a> |
<a href="/github" rel="noopener">Github</a> |
<a href="/linkedin" rel="noopener">Linkedin</a> |
<a href="https://lunaseeker.com/newsletter/" rel="noopener">Newsletter</a> |
<a href="https://lunaseeker.com/offerings" rel="noopener">Offerings</a> |
<a href="https://lunaseeker.com" rel="noopener">Portfolio</a>
</p>
</footer>
</body>
</html>