- Oct 2024
-
developer.mozilla.org developer.mozilla.org
-
can't be represented exactly by a float
This is saying why
(2.55).toFixed(1)
evaluates to 2.5, cuz, internally, 2.55 is actually, 2.5499999999..., which evals to 2.5 indeed.
-
- Jul 2024
-
learnxinyminutes.com learnxinyminutes.com
-
Note that 0 is falsy and "0" is truthy, even though 0 == "0"
JavaScript 中的相等运算符 == 会进行自动类型转换, ===不会
-
-
developer.mozilla.org developer.mozilla.org
-
The with() method always creates a dense array.
This could be a good way to create a new array instead of Array.from(new Array(x).keys())
-
- Jun 2024
-
www.second.dev www.second.dev
- Dec 2023
- Nov 2023
-
moment.github.io moment.github.io
-
-
flaviocopes.com flaviocopes.com
-
```js import path from 'path'; import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); ```
Tags
Annotators
URL
-
-
alan.norbauer.com alan.norbauer.com
Tags
Annotators
URL
-
-
developer.chrome.com developer.chrome.com
-
<div itemscope itemtype="http://schema.org/Code"> ```js let db; const DBOpenRequest = window.indexedDB.open("toDoList", 4); DBOpenRequest.onsuccess = (event) => { db = DBOpenRequest.result; }; const transaction = db.transaction( ['toDoList'], 'readwrite', { durability: 'strict' }); ``` </div>
-
-
developer.mozilla.org developer.mozilla.org
- Oct 2023
-
webauthn.wtf webauthn.wtf
Tags
Annotators
URL
-
-
github.com github.com
-
infra.spec.whatwg.org infra.spec.whatwg.org
-
-
research.securitum.com research.securitum.com
-
blacksmithgu.github.io blacksmithgu.github.io
-
nota-lang.org nota-lang.org
-
-
nota-lang.org nota-lang.org
Tags
Annotators
URL
-
-
Tags
Annotators
URL
-
-
vega.github.io vega.github.io
-
www.sitepoint.com www.sitepoint.com
Tags
Annotators
URL
-
-
pugjs.org pugjs.org
Tags
Annotators
URL
-
-
-
Tags
Annotators
URL
-
-
developer.mozilla.org developer.mozilla.org
-
```js const url = "https://path_to_large_file.mp4";
try { const res = await fetch(url, { signal: AbortSignal.timeout(5000) }); const result = await res.blob(); // … } catch (err) { if (err.name === "TimeoutError") { console.error("Timeout: It took more than 5 seconds to get the result!"); } else if (err.name === "AbortError") { console.error( "Fetch aborted by user action (browser stop button, closing tab, etc.", ); } else if (err.name === "TypeError") { console.error("AbortSignal.timeout() method is not supported"); } else { // A network error, or some other problem. console.error(
Error: type: ${err.name}, message: ${err.message}
); } } ```
-
-
-
-
ns.inria.fr ns.inria.frLDScript1
Tags
Annotators
URL
-
- Sep 2023
-
chromestatus.com chromestatus.com
-
gist.github.com gist.github.com
-
-
permit streams to be transferred between workers, frames and anywhere else that postMessage() can be used. Chunks can be anything which is cloneable by postMessage(). Initially chunks enqueued in such a stream will always be cloned, ie. all data will be copied. Future work will extend the Streams APIs to support transferring objects (ie. zero copy).
js const rs = new ReadableStream({ start(controller) { controller.enqueue('hello'); } }); const w = new Worker('worker.js'); w.postMessage(rs, [rs]);
js onmessage = async (evt) => { const rs = evt.data; const reader = rs.getReader(); const {value, done} = await reader.read(); console.log(value); // logs 'hello'. };
-
-
hachyderm.io hachyderm.io
Tags
Annotators
URL
-
-
blog.bitsrc.io blog.bitsrc.io
-
www.digitalocean.com www.digitalocean.com
-
Modules do not add anything to the global (window) scope. Modules always are in strict mode. Loading the same module twice in the same file will have no effect, as modules are only executed once. Modules require a server environment.
-
-
twitter.com twitter.com
-
Did you know you can now use streaming promises in SvelteKit? No need to import anything - it just works out of the box
Every property of an object returned by the
load
function can be a promise which itself can return an object that can have a promise as property, and so on.Can build a tree of promises based on data delivery priority.
-
-
stackoverflow.com stackoverflow.com
-
-
webgl-shaders.com webgl-shaders.com
Tags
Annotators
URL
-
-
www.theregister.com www.theregister.com
-
If a site you visit queries the Topics API, it may learn of this interest from Chrome and decide to serve you an advert about bonds or retirement funds. It also means websites can fetch your online interests straight from your browser.
The Topics API is worst than 3rd-parties cookies, anyone can query a user ad profile:
```js // document.browsingTopics() returns an array of BrowsingTopic objects. const topics = await document.browsingTopics();
// Get data for an ad creative. const response = await fetch('https://ads.example/get-creative', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(topics) });
// Get the JSON from the response. const creative = await response.json();
// Display the ad. (or not) ```
-
-
cheatsheetseries.owasp.org cheatsheetseries.owasp.org
-
stackoverflow.com stackoverflow.com
- Aug 2023
-
www.youtube.com www.youtube.com
-
-
telefunc.com telefunc.comTelefunc1
-
-
stackoverflow.com stackoverflow.com
-
```js function createPromiseWithData() { let resolveFn;
const promise = new Promise((resolve, reject) => { resolveFn = resolve; });
return { promise, resolveFn }; }
// Usage const { promise, resolveFn } = createPromiseWithData();
// Later, when you have the data you want to pass const data = 'Future data';
// Resolve the promise with the data resolveFn(data);
// Use the promise promise.then((result) => { console.log('Promise resolved with:', result); }); ```
-
-
vitejs.dev vitejs.devVite1
-
-
vitejs.dev vitejs.devVite1
-
-
-
Add aliases to
vite.config.ts
```js import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import path from 'path';
// https://vitejs.dev/config/ export default defineConfig({ resolve: { alias: { '@': path.resolve(__dirname, './src'), '@assets': path.resolve(__dirname, './src/assets'), '@components': path.resolve(__dirname, './src/components'), }, }, plugins: [react()] }) ```
Add aliases to
tsconfig.json
```js { "compilerOptions": { // ... your other compiler options "baseUrl": ".", "paths": { "@/": ["src/"], "@components/": ["src/components/"], "@assets/": ["src/assets/"] }, }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] } ````
Use alias
js import image from `@assets/image.png`
Tags
Annotators
URL
-
-
web-platform-tests.org web-platform-tests.org
-
developer.chrome.com developer.chrome.com
Tags
Annotators
URL
-
-
www.oddbird.net www.oddbird.net
Tags
Annotators
URL
-
-
web-platform-tests.org web-platform-tests.org
Tags
Annotators
URL
-
-
blog.logrocket.com blog.logrocket.com
-
developer.mozilla.org developer.mozilla.org
-
developer.chrome.com developer.chrome.com
Tags
Annotators
URL
-
-
wicg.github.io wicg.github.io
Tags
Annotators
URL
-
-
developer.chrome.com developer.chrome.com
Tags
Annotators
URL
-
-
github.com github.com
-
A spec to optimize ad targeting (respectful of privacy, they say... 😂🤣).
Fuck you Google with your dystopian API:
```js // document.browsingTopics() returns an array of BrowsingTopic objects. const topics = await document.browsingTopics();
// Get data for an ad creative. const response = await fetch('https://ads.example/get-creative', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(topics) });
// Get the JSON from the response. const creative = await response.json();
// Display the ad. (or not) ```
-
-
developer.chrome.com developer.chrome.com
-
Call the Topics API from within that iframe:
const topics = await document.browsingTopics();
-
-
Tags
Annotators
URL
-
-
-
```js // Create a portal with the wikipedia page, and embed it // (like an iframe). You can also use the <portal> tag instead. portal = document.createElement('portal'); portal.src = 'https://en.wikipedia.org/wiki/World_Wide_Web'; portal.style = '...'; document.body.appendChild(portal);
// When the user touches the preview (embedded portal): // do fancy animation, e.g. expand … // and finish by doing the actual transition. // For the sake of simplicity, this snippet will navigate // on the
onload
event of the Portals element. portal.addEventListener('load', (evt) => { portal.activate(); });// Adding some styles with transitions const style = document.createElement('style'); style.innerHTML =
portal { position:fixed; width: 100%; height: 100%; opacity: 0; box-shadow: 0 0 20px 10px #999; transform: scale(0.4); transform-origin: bottom left; bottom: 20px; left: 20px; animation-name: fade-in; animation-duration: 1s; animation-delay: 2s; animation-fill-mode: forwards; } .portal-transition { transition: transform 0.4s; } @media (prefers-reduced-motion: reduce) { .portal-transition { transition: transform 0.001s; } } .portal-reveal { transform: scale(1.0) translateX(-20px) translateY(20px); } @keyframes fade-in { 0% { opacity: 0; } 100% { opacity: 1; } }
; const portal = document.createElement('portal'); // Let's navigate into the WICG Portals spec page portal.src = 'https://wicg.github.io/portals/'; // Add a class that defines the transition. Consider using //prefers-reduced-motion
media query to control the animation. // https://developers.google.com/web/updates/2019/03/prefers-reduced-motion portal.classList.add('portal-transition'); portal.addEventListener('click', (evt) => { // Animate the portal once user interacts portal.classList.add('portal-reveal'); }); portal.addEventListener('transitionend', (evt) => { if (evt.propertyName == 'transform') { // Activate the portal once the transition has completed portal.activate(); } }); document.body.append(style, portal); ``````js // Feature detection
if ('HTMLPortalElement' in window) { // If this is a platform that have Portals... const portal = document.createElement('portal'); ... } ```
js // Detect whether this page is hosted in a portal if (window.portalHost) { // Customize the UI when being embedded as a portal }
```js // Send message to the portal element const portal = document.querySelector('portal'); portal.postMessage({someKey: someValue}, ORIGIN);
// Receive message via window.portalHost window.portalHost.addEventListener('message', (evt) => { const data = evt.data.someKey; // handle the event }); ```
Tags
Annotators
URL
-
-
wicg.github.io wicg.github.ioPortals1
Tags
Annotators
URL
-
-
developer.mozilla.org developer.mozilla.org
-
```html
<header>Movie website
<search> <form action="./search/"> <label for="movie">Find a Movie</label> <input type="search" id="movie" name="q" /> <button type="submit">Search</button> </form> </search> </header>```
```html <search> <label> Find and filter your query <input type="search" id="query" /> </label> <label> <input type="checkbox" id="exact-only" /> Exact matches only </label>
<section>Results:
</search> ```
-
-
-
-
authjs.dev authjs.devAuth.js2
Tags
Annotators
URL
-
-
cheatsheetseries.owasp.org cheatsheetseries.owasp.org
-
developer.mozilla.org developer.mozilla.org
-
www.w3.org www.w3.org
-
twitter.com twitter.com
Tags
Annotators
URL
-
-
stackoverflow.com stackoverflow.com
-
stackoverflow.com stackoverflow.com
-
-
```html
<body style="visibility: hidden;"> <script>0</script> </body> <noscript> <style>body { visibility: visible; }</style> </noscript>```
-
- Jul 2023
-
developer.chrome.com developer.chrome.com
-
html <meta http-equiv="Accept-CH" content="DPR, Viewport-Width, Width"> ... <picture> <!-- serve WebP to Chrome and Opera --> <source media="(min-width: 50em)" sizes="50vw" srcset="/image/thing-200.webp 200w, /image/thing-400.webp 400w, /image/thing-800.webp 800w, /image/thing-1200.webp 1200w, /image/thing-1600.webp 1600w, /image/thing-2000.webp 2000w" type="image/webp"> <source sizes="(min-width: 30em) 100vw" srcset="/image/thing-crop-200.webp 200w, /image/thing-crop-400.webp 400w, /image/thing-crop-800.webp 800w, /image/thing-crop-1200.webp 1200w, /image/thing-crop-1600.webp 1600w, /image/thing-crop-2000.webp 2000w" type="image/webp"> <!-- serve JPEGXR to Edge --> <source media="(min-width: 50em)" sizes="50vw" srcset="/image/thing-200.jpgxr 200w, /image/thing-400.jpgxr 400w, /image/thing-800.jpgxr 800w, /image/thing-1200.jpgxr 1200w, /image/thing-1600.jpgxr 1600w, /image/thing-2000.jpgxr 2000w" type="image/vnd.ms-photo"> <source sizes="(min-width: 30em) 100vw" srcset="/image/thing-crop-200.jpgxr 200w, /image/thing-crop-400.jpgxr 400w, /image/thing-crop-800.jpgxr 800w, /image/thing-crop-1200.jpgxr 1200w, /image/thing-crop-1600.jpgxr 1600w, /image/thing-crop-2000.jpgxr 2000w" type="image/vnd.ms-photo"> <!-- serve JPEG to others --> <source media="(min-width: 50em)" sizes="50vw" srcset="/image/thing-200.jpg 200w, /image/thing-400.jpg 400w, /image/thing-800.jpg 800w, /image/thing-1200.jpg 1200w, /image/thing-1600.jpg 1600w, /image/thing-2000.jpg 2000w"> <source sizes="(min-width: 30em) 100vw" srcset="/image/thing-crop-200.jpg 200w, /image/thing-crop-400.jpg 400w, /image/thing-crop-800.jpg 800w, /image/thing-crop-1200.jpg 1200w, /image/thing-crop-1600.jpg 1600w, /image/thing-crop-2000.jpg 2000w"> <!-- fallback for browsers that don't support picture --> <img src="/image/thing.jpg" width="50%"> </picture>
-
-
developer.chrome.com developer.chrome.com
-
```js // Log the full user-agent data navigator .userAgentData.getHighEntropyValues( ["architecture", "model", "bitness", "platformVersion", "fullVersionList"]) .then(ua => { console.log(ua) });
// output { "architecture":"x86", "bitness":"64", "brands":[ { "brand":" Not A;Brand", "version":"99" }, { "brand":"Chromium", "version":"98" }, { "brand":"Google Chrome", "version":"98" } ], "fullVersionList":[ { "brand":" Not A;Brand", "version":"99.0.0.0" }, { "brand":"Chromium", "version":"98.0.4738.0" }, { "brand":"Google Chrome", "version":"98.0.4738.0" } ], "mobile":false, "model":"", "platformVersion":"12.0.1" } ```
Tags
- cito:cites=urn:ietf:rfc:1945
- cito:cites=urn:ietf:rfc:8942
- http:header=sec-ch-ua-full-version-list
- http:header=sec-ch-ua-bitness
- js
- http:header=sec-ch-ua-arch
- http:header=sec-ch-ua-model
- http:header=sec-ch-ua
- http:header=sec-ch-ua-full-version
- conneg
- http:header=sec-ch-ua-mobile
- http:header=user-agent
- mobile
- http:header=sec-ch-ua-platform-version
- <meta http-equiv="accept-ch"/>
- wikipedia:en=HTTP_Client_Hints
- http
- http:header=accept-ch
- http:header=sec-ch-ua-platform
Annotators
URL
-
-
-
Good case study in class of JS/Node category errors.
Tags
Annotators
URL
-
-
Tags
Annotators
URL
-
-
developer.mozilla.org developer.mozilla.org
-
w3c.github.io w3c.github.io
Tags
Annotators
URL
-
-
www.netlify.com www.netlify.com
-
blog.logrocket.com blog.logrocket.com
Tags
Annotators
URL
-
-
geoffrich.net geoffrich.net
-
```html
<script> let counter = 0; let evens = 0; $: { if (counter > 10) { break $; } if (counter % 2 === 0) { evens++; } } </script><button on:click={() => (counter++)}> Increment </button>
Counter: {counter}, evens before 10: {evens}
```
Tags
Annotators
URL
-
-
kit.svelte.dev kit.svelte.dev
Tags
Annotators
URL
-
-
svelte.dev svelte.dev
-
-
developer.chrome.com developer.chrome.com
-
blog.cloudflare.com blog.cloudflare.com
-
gist.github.com gist.github.com
-
pwa-workshop.js.org pwa-workshop.js.org
-
-
gist.github.com gist.github.com
-
```js / * twitter-entities.js * This function converts a tweet with "entity" metadata * from plain text to linkified HTML. * * See the documentation here: http://dev.twitter.com/pages/tweet_entities * Basically, add ?include_entities=true to your timeline call * * Copyright 2010, Wade Simmons * Licensed under the MIT license * http://wades.im/mons * * Requires jQuery /
function escapeHTML(text) { return $('<div/>').text(text).html() }
function linkify_entities(tweet) { if (!(tweet.entities)) { return escapeHTML(tweet.text) }
// This is very naive, should find a better way to parse this var index_map = {} $.each(tweet.entities.urls, function(i,entry) { index_map[entry.indices[0]] = [entry.indices[1], function(text) {return "<a href='"+escapeHTML(entry.url)+"'>"+escapeHTML(text)+"</a>"}] }) $.each(tweet.entities.hashtags, function(i,entry) { index_map[entry.indices[0]] = [entry.indices[1], function(text) {return "<a href='http://twitter.com/search?q="+escape("#"+entry.text)+"'>"+escapeHTML(text)+"</a>"}] }) $.each(tweet.entities.user_mentions, function(i,entry) { index_map[entry.indices[0]] = [entry.indices[1], function(text) {return "<a title='"+escapeHTML(entry.name)+"' href='http://twitter.com/"+escapeHTML(entry.screen_name)+"'>"+escapeHTML(text)+"</a>"}] }) var result = "" var last_i = 0 var i = 0 // iterate through the string looking for matches in the index_map for (i=0; i < tweet.text.length; ++i) { var ind = index_map[i] if (ind) { var end = ind[0] var func = ind[1] if (i > last_i) { result += escapeHTML(tweet.text.substring(last_i, i)) } result += func(tweet.text.substring(i, end)) i = end - 1 last_i = end } } if (i > last_i) { result += escapeHTML(tweet.text.substring(last_i, i)) } return result
} ```
Tags
Annotators
URL
-
-
developer.mozilla.org developer.mozilla.org
-
```js if (navigator.mediaDevices) { console.log("getUserMedia supported.");
const constraints = { audio: true }; let chunks = [];
navigator.mediaDevices .getUserMedia(constraints) .then((stream) => { const mediaRecorder = new MediaRecorder(stream);
visualize(stream); record.onclick = () => { mediaRecorder.start(); console.log(mediaRecorder.state); console.log("recorder started"); record.style.background = "red"; record.style.color = "black"; }; stop.onclick = () => { mediaRecorder.stop(); console.log(mediaRecorder.state); console.log("recorder stopped"); record.style.background = ""; record.style.color = ""; }; mediaRecorder.onstop = (e) => { console.log("data available after MediaRecorder.stop() called."); const clipName = prompt("Enter a name for your sound clip"); const clipContainer = document.createElement("article"); const clipLabel = document.createElement("p"); const audio = document.createElement("audio"); const deleteButton = document.createElement("button"); clipContainer.classList.add("clip"); audio.setAttribute("controls", ""); deleteButton.textContent = "Delete"; clipLabel.textContent = clipName; clipContainer.appendChild(audio); clipContainer.appendChild(clipLabel); clipContainer.appendChild(deleteButton); soundClips.appendChild(clipContainer); audio.controls = true; const blob = new Blob(chunks, { type: "audio/ogg; codecs=opus" }); chunks = []; const audioURL = URL.createObjectURL(blob); audio.src = audioURL; console.log("recorder stopped"); deleteButton.onclick = (e) => { const evtTgt = e.target; evtTgt.parentNode.parentNode.removeChild(evtTgt.parentNode); }; }; mediaRecorder.ondataavailable = (e) => { chunks.push(e.data); }; }) .catch((err) => { console.error(`The following error occurred: ${err}`); });
} ```
-
-
developer.mozilla.org developer.mozilla.org
-
```js const canvas = document.querySelector("canvas");
// Optional frames per second argument. const stream = canvas.captureStream(25); const recordedChunks = [];
console.log(stream); const options = { mimeType: "video/webm; codecs=vp9" }; const mediaRecorder = new MediaRecorder(stream, options);
mediaRecorder.ondataavailable = handleDataAvailable; mediaRecorder.start();
function handleDataAvailable(event) { console.log("data-available"); if (event.data.size > 0) { recordedChunks.push(event.data); console.log(recordedChunks); download(); } else { // … } } function download() { const blob = new Blob(recordedChunks, { type: "video/webm", }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); document.body.appendChild(a); a.style = "display: none"; a.href = url; a.download = "test.webm"; a.click(); window.URL.revokeObjectURL(url); }
// demo: to download after 9sec setTimeout((event) => { console.log("stopping"); mediaRecorder.stop(); }, 9000); ```
-
-
www.w3.org www.w3.org
Tags
Annotators
URL
-
-
developers.cloudflare.com developers.cloudflare.com
-
developers.cloudflare.com developers.cloudflare.com
-
js export default { async tail(events) { fetch("https://example.com/endpoint", { method: "POST", body: JSON.stringify(events), }) } }
-
-
danielrotter.at danielrotter.at
-
Tags
Annotators
URL
-
-
www.youtube.com www.youtube.com
-
-
www.skcript.com www.skcript.com
Tags
Annotators
URL
-
-
javascript.plainenglish.io javascript.plainenglish.io
-
legendapp.com legendapp.com
Tags
Annotators
URL
-
-
twitter.com twitter.com
Tags
Annotators
URL
-
-
blog.axlight.com blog.axlight.com
-
codedamn.com codedamn.com
Tags
Annotators
URL
-
-
developers.cloudflare.com developers.cloudflare.com
-
js export default { async scheduled(event, env, ctx) { ctx.waitUntil(doSomeTaskOnASchedule()); }, };
-
-
developer.mozilla.org developer.mozilla.org
-
A labeled statement is any statement that is prefixed with an identifier. You can jump to this label using a break or continue statement nested within the labeled statement
Using a labeled continue with for loops
``js // The first for statement is labeled "loop1" loop1: for (let i = 0; i < 3; i++) { // The second for statement is labeled "loop2" loop2: for (let j = 0; j < 3; j++) { if (i === 1 && j === 1) { continue loop1; } console.log(
i = ${i}, j = ${j}`); } }// Logs: // i = 0, j = 0 // i = 0, j = 1 // i = 0, j = 2 // i = 1, j = 0 // i = 2, j = 0 // i = 2, j = 1 // i = 2, j = 2 ```
Using a labeled break with for loops
```js let i, j;
// The first for statement is labeled "loop1" loop1: for (i = 0; i < 3; i++) { // The second for statement is labeled "loop2" loop2: for (j = 0; j < 3; j++) { if (i === 1 && j === 1) { break loop1; } console.log(
i = ${i}, j = ${j}
); } }// Logs: // i = 0, j = 0 // i = 0, j = 1 // i = 0, j = 2 // i = 1, j = 0 ```
Using a labeled continue statement
```js // Numbers from 1 to 100 const items = Array.from({ length: 100 }, (_, i) => i + 1)); const tests = [ { pass: (item) => item % 2 === 0 }, { pass: (item) => item % 3 === 0 }, { pass: (item) => item % 5 === 0 }, ]; let itemsPassed = 0;
itemIteration: for (const item of items) { for (const test of tests) { if (!test.pass(item)) { continue itemIteration; } }
itemsPassed++; }
js // Numbers from 1 to 100 const items = Array.from({ length: 100 }, (_, i) => i + 1)); const tests = [ { pass: (item) => item % 2 === 0 }, { pass: (item) => item % 3 === 0 }, { pass: (item) => item % 5 === 0 }, ]; let itemsPassed = 0;
for (const item of items) { let passed = true; for (const test of tests) { if (!test.pass(item)) { passed = false; break; } } if (passed) { itemsPassed++; } } ```
Using a labeled break statement
```js // Numbers from 1 to 100 const items = Array.from({ length: 100 }, (_, i) => i + 1)); const tests = [ { pass: (item) => item % 2 === 0 }, { pass: (item) => item % 3 === 0 }, { pass: (item) => item % 5 === 0 }, ]; let allPass = true;
itemIteration: for (const item of items) { for (const test of tests) { if (!test.pass(item)) { allPass = false; break itemIteration; } } } ```
Using a labeled block with break
```js foo: { console.log("face"); break foo; console.log("this will not be executed"); } console.log("swap");
// Logs: // "face" // "swap" ```
-
-
chromestatus.com chromestatus.com
-
Adds "previousslide" and "nextslide" actions to the existing Media Session API.
This will enable developers of video conferencing websites to handle these actions from browser UI. For example, if the user puts their slides presentation into a picture-in-picture window, the browser could display buttons for navigating through slides. When the user clicks those, the website handles them through the Media Session API.
-
-
googlechrome.github.io googlechrome.github.io
-
```js let slideNumber = 1;
togglePipButton.addEventListener("click", async () => { try { if (video !== document.pictureInPictureElement) await video.requestPictureInPicture(); else await document.exitPictureInPicture(); } catch (error) { log(
> Argh! ${error}
); } });try { navigator.mediaSession.setActionHandler("previousslide", () => { log('> User clicked "Previous Slide" icon.'); if (slideNumber > 1) slideNumber--; updateSlide(); }); } catch (error) { log('Warning! The "previousslide" media session action is not supported.'); }
try { navigator.mediaSession.setActionHandler("nextslide", () => { log('> User clicked "Next Slide" icon.'); slideNumber++; updateSlide(); }); } catch (error) { log('Warning! The "nextslide" media session action is not supported.'); }
/ Picture-in-Picture canvas /
const canvas = document.querySelector("canvas"); canvas.width = 1024; canvas.height = 512; updateSlide();
const video = document.createElement("video"); video.srcObject = canvas.captureStream(); video.muted = true; video.play();
/ Utils /
function updateSlide() { const ctx = canvas.getContext("2d"); ctx.fillStyle = "grey"; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = "white"; ctx.font = "100px Google Sans,arial,sans-serif"; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText(
slide ${slideNumber}
, canvas.width / 2, canvas.height / 2, canvas.width); } ```
-
-
developers.cloudflare.com developers.cloudflare.com
Tags
Annotators
URL
-
-
developers.cloudflare.com developers.cloudflare.com
Tags
Annotators
URL
-
-
blog.cloudflare.com blog.cloudflare.com
Tags
Annotators
URL
-
-
developer.chrome.com developer.chrome.com
-
stackoverflow.com stackoverflow.com
-
js 'good_luck_buddy'.split(/_(.*)/s) ['good', 'luck_buddy', ''] // ignore the third element
-
-
github.com github.com
-
```js // Getting details on a Threads user and outputting it to the console
const getUserDetails = async (username) => { let userInfo = await threads.getUserData(username); console.log( "",
Name: ${userInfo.full_name}\n
,Bio: ${userInfo.biography}\n
,ID: ${userInfo.user_id}\n
,Followers: ${userInfo.follower_count}\n
,Website: ${userInfo.bio_links[0].url}
); } getUserDetails("Gisgar3"); ```
Tags
Annotators
URL
-
-
github.com github.com
-
```js import { ThreadsAPI } from 'threads-api';
// or in Deno 🦖: // import { ThreadsAPI } from "npm:threads-api";
const main = async () => { const threadsAPI = new ThreadsAPI();
const username = '_junhoyeo'; const id = await threadsAPI.getUserIDfromUsername(username); console.log(id);
if (!id) { return; }
const user = await threadsAPI.getUserProfile(username, id); console.log(JSON.stringify(user));
const posts = await threadsAPI.getUserProfileThreads(username, id); console.log(JSON.stringify(posts));
const replies await threadsAPI.getUserProfileReplies(username, id); console.log(JSON.stringify(replies)); }; main(); ```
Tags
Annotators
URL
-
-
dev.to dev.to
-
js function getFlagEmoji(countryCode) { const codePoints = countryCode .toUpperCase() .split('') .map(char => 127397 + char.charCodeAt()); return String.fromCodePoint(...codePoints); }
js getFlagEmoji('US') = 🇺🇸 getFlagEmoji('NL') = 🇳🇱 getFlagEmoji('CH') = 🇨🇭
-
-
developer.mozilla.org developer.mozilla.org
-
github.com github.com
Tags
Annotators
URL
-
-
developers.cloudflare.com developers.cloudflare.com
-
developers.cloudflare.com developers.cloudflare.com
-
WebSocket support in Pub/Sub works by encapsulating MQTT packets (Pub/Sub’s underlying native protocol) within WebSocket framesExternal link icon
```js // Ensure MQTT.js is installed first // > npm install mqtt import * as mqtt from "mqtt"
// Where 'url' is "mqtts://BROKER.NAMESPACE.cloudflarepubsub.com:8884" function example(url) { let client = mqtt.connect(url, { protocolVersion: 5, reconnectPeriod: 0, username: 'anything', password: jwt, // pass this from a form field in your app clientId: '', })
client.on('connect', function () { client.subscribe(topic, function (err) { if (err) { client.end(); } else { console.log(
subscribed to ${topic}
) } })client.on('message', function (topic, message) { let line = (new Date()).toLocaleString('en-US') + ": " + message.toString() + "\n"; console.log(line) }) } ```
-
-
-
-
-
```js exports.handler = async (event) => { if (event.headers != undefined) { const headers = toLowerCaseProperties(event.headers);
if (headers['sec-websocket-protocol'] != undefined) { const subprotocolHeader = headers['sec-websocket-protocol']; const subprotocols = subprotocolHeader.split(','); if (subprotocols.indexOf('myprotocol') >= 0) { const response = { statusCode: 200, headers: { "Sec-WebSocket-Protocol" : "myprotocol" } }; return response; } } } const response = { statusCode: 400 }; return response;
};function toLowerCaseProperties(obj) { var wrapper = {}; for (var key in obj) { wrapper[key.toLowerCase()] = obj[key]; } return wrapper; } ```
-
-
developers.cloudflare.com developers.cloudflare.com
-
stackoverflow.com stackoverflow.com
-
```js async function main() { const blob = new Blob([new Uint8Array(10 * 1024 * 1024)]); // any Blob, including a File const uploadProgress = document.getElementById("upload-progress"); const downloadProgress = document.getElementById("download-progress");
const totalBytes = blob.size; let bytesUploaded = 0;
// Use a custom TransformStream to track upload progress const progressTrackingStream = new TransformStream({ transform(chunk, controller) { controller.enqueue(chunk); bytesUploaded += chunk.byteLength; console.log("upload progress:", bytesUploaded / totalBytes); uploadProgress.value = bytesUploaded / totalBytes; }, flush(controller) { console.log("completed stream"); }, }); const response = await fetch("https://httpbin.org/put", { method: "PUT", headers: { "Content-Type": "application/octet-stream" }, body: blob.stream().pipeThrough(progressTrackingStream), duplex: "half", });
// After the initial response headers have been received, display download progress for the response body let success = true; const totalDownloadBytes = response.headers.get("content-length"); let bytesDownloaded = 0; const reader = response.body.getReader(); while (true) { try { const { value, done } = await reader.read(); if (done) { break; } bytesDownloaded += value.length; if (totalDownloadBytes != undefined) { console.log("download progress:", bytesDownloaded / totalDownloadBytes); downloadProgress.value = bytesDownloaded / totalDownloadBytes; } else { console.log("download progress:", bytesDownloaded, ", unknown total"); } } catch (error) { console.error("error:", error); success = false; break; } }
console.log("success:", success); } main().catch(console.error); ```
-
-
-
On any Web page run the following code
js await startLocalServer(); let abortable = new AbortController; let {signal} = abortable; (await fetch('https://localhost:8443', { method: 'post', body: 'cat local_server_export.js', // Code executed in server, piped to browser duplex: 'half', headers: { 'Access-Control-Request-Private-Network': true }, signal })).body.pipeThrough(new TextDecoderStream()).pipeTo(new WritableStream({ write(v) { console.log(v); }, close() { console.log('close'); }, abort(reason) { console.log(reason); } })).catch(console.warn); await resetLocalServer();
-
-
docs.npmjs.com docs.npmjs.com
Tags
Annotators
URL
-
-
Tags
Annotators
URL
-
-
developer.mozilla.org developer.mozilla.org
-
www.w3.org www.w3.org
Tags
Annotators
URL
-
-
developers.cloudflare.com developers.cloudflare.com
-
The Hibernation API allows a Durable Object that is not currently running an event handler, such as handling a WebSocket message, HTTP request, or alarm, to be removed from memory while keeping its WebSockets connected (“hibernation”).
-
-
blog.scottlogic.com blog.scottlogic.com
-
stackoverflow.com stackoverflow.com
-
www.sencha.com www.sencha.com
Tags
Annotators
URL
-
- Jun 2023
-
wdtaxonomy.readthedocs.io wdtaxonomy.readthedocs.io
-
postcss.org postcss.org
Tags
Annotators
URL
-
-
medium.engineering medium.engineering
-
```js FastMutex.prototype.runInLock = function (callback, opt_context) { this._setX()
if (!this._isLockAvailable()) { this._retry(callback, opt_context) return }
this._setY()
if (this._getX() != this._clientId) { window.setTimeout(function () { if (this.hasLock()) this._execute(callback, opt_context) else this._retry(callback, opt_context) }.bind(this), Math.round(Math.random() * 100))
} else { this._execute(callback, opt_context) } }
FastMutex.prototype._execute = function (callback, opt_context) { var rv try { rv = callback.call(opt_context) } finally { if (rv instanceof goog.async.Deferred) { rv.addFinally(this._clearLock, this) } else { this._clearLock() } } } ```
-
-
sketch.systems sketch.systems
Tags
Annotators
URL
-
-
sketch.systems sketch.systems
-
www.totaltypescript.com www.totaltypescript.com
Tags
Annotators
URL
-
-
-
```html
<body> <div> {% for chat in chats %} <div>{{ chat.contents }}</div> <button id={{chat.id}} ✅ onClick=getId(id) ✅ > print this chat_id out </button> {% endfor %} </div> ... <script> function getId(id){ console.log(id) } </script> </body>```
-
-
jakearchibald.com jakearchibald.com
-
-
github.com github.com
-
nodejs cmd line
-
-
-
```js const authChannel = new BroadcastChannel('authChannel');
authChannel.addEventListener('message', () => { logout(); })
export function logout() { // logout logic }
logoutButton.addEventListener('click', () => { authChannel.postMessage('logout'); logout(); }); ```
```js import { render } from 'solid-js/web'; import { createSignal, For, onCleanup } from 'solid-js';
const todosChannel = new BroadcastChannel('todos');
function Todos() { const listener = todosChannel.addEventListener('message', e => { setTodos(e.data) });
const [todos, setTodos] = createSignal(['One']);
const addTodo = () => { setTodos(todos => [...todos, Math.random().toString()]); todosChannel.postMessage(todos()); }
onCleanup(() => { todosChannel.removeEventListener('message', listener); })
return ( <> <button onClick={addTodo}>Add Todo</button>
<For each={todos()}> {(item) => <div>{item}</div>} </For>
) }
render(() => <Todos />, document.getElementById('root')); ```
-
-
developer.chrome.com developer.chrome.com
-
Tags
Annotators
URL
-
-
bugzilla.mozilla.org bugzilla.mozilla.org
Tags
Annotators
URL
-
-
github.com github.com
-
gist.github.com gist.github.com
-
```js 'use strict';
// --------------------------------------------------------------------
class Cache extends Map { constructor(key) { super() this._key = key this.load() }
set(k, v) { if (!this.has(k) || v !== this.get(k)) { super.set(k, v) this.save() } }
delete(k) { if (super.has(k)) { super.delete(k) this.save() } }
clear() { if (super.size() > 0) { super.clear() this.save() } }
json() { let obj = Object.create(null)
for (let k of this.keys()) { obj[k] = this.get(k) } return obj
}
save() { const data = this.json() this._save(this._key, data) this.dump({ data, event: 'SAVE' }) }
load(s) { const data = this._load(this._key)
if (typeof data !== 'object') { return } super.clear() for (let k of Object.keys(data)) { super.set(k, data[k]) } this.dump({ data, event: 'LOAD' })
}
_save(key, data) {} _load(key) {}
edit() { let res = window.prompt('Edit cached package URLs', JSON.stringify(this.json(), null, 2))
if (res !== null) { try { const data = JSON.parse(res) super.clear() for (let k of Object.keys(data)) { super.set(k, data[k]) } this.save() } catch (ex) { console.warn('Failed to update cache data: %s %o', ex.toString(), ex) } }
}
toString() { return
${this.constructor.name}<${this._key}>: keys=[ ${this.keys().sort().join(', ')} ]
}dump({ data, event }) { console.group(
${this.constructor.name}<${this._key}>: ${event || 'STATE'}:
) console.info(JSON.stringify(data || this.json(), null, 2)) console.groupEnd() } }// --------------------------------------------------------------------
class GMCache extends Cache { _save(key, data) { GM_setValue(key, JSON.stringify(data || {})) }
_load(key) { return JSON.parse(GM_getValue(key) || '{}') } }
// --------------------------------------------------------------------
class StorageCache extends Cache { constructor(key, session) { super(key)
this.storage = session ? window.sessionStorage : window.localStorage this.load()
}
_save(key, data) { this.storage.setItem(key, JSON.stringify(data || {})) }
_load(key) { if (this.storage) { return JSON.parse(this.storage.getItem(key) || '{}') } } }
// --------------------------------------------------------------------
let c = new StorageCache('test-data') // c.set('jshint-summary', { name: 'jshint-summary', homepage: 'https://github.com/spiralx/jshint-summary' }) ```
-
-
-
```js /* * Response from cache / self.addEventListener('fetch', event => { const response = self.caches.open('example') .then(caches => caches.match(event.request)) .then(response => response || fetch(event.request));
event.respondWith(response); });
/* * Response to SSE by text / self.addEventListener('fetch', event => { const { headers } = event.request; const isSSERequest = headers.get('Accept') === 'text/event-stream';
if (!isSSERequest) { return; }
event.respondWith(new Response('Hello!')); });
/* * Response to SSE by stream / self.addEventListener('fetch', event => { const { headers } = event.request; const isSSERequest = headers.get('Accept') === 'text/event-stream';
if (!isSSERequest) { return; }
const responseText = 'Hello!'; const responseData = Uint8Array.from(responseText, x => x.charCodeAt(0)); const stream = new ReadableStream({ start: controller => controller.enqueue(responseData) }); const response = new Response(stream);
event.respondWith(response); });
/* * SSE chunk data / const sseChunkData = (data, event, retry, id) => Object.entries({ event, id, data, retry }) .filter(([, value]) => ![undefined, null].includes(value)) .map(([key, value]) =>
${key}: ${value}
) .join('\n') + '\n\n';/* * Success response to SSE from SW / self.addEventListener('fetch', event => { const { headers } = event.request; const isSSERequest = headers.get('Accept') === 'text/event-stream';
if (!isSSERequest) { return; }
const sseChunkData = (data, event, retry, id) => Object.entries({ event, id, data, retry }) .filter(([, value]) => ![undefined, null].includes(value)) .map(([key, value]) =>
${key}: ${value}
) .join('\n') + '\n\n';const sseHeaders = { 'content-type': 'text/event-stream', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', };
const responseText = sseChunkData('Hello!'); const responseData = Uint8Array.from(responseText, x => x.charCodeAt(0)); const stream = new ReadableStream({ start: controller => controller.enqueue(responseData) }); const response = new Response(stream, { headers: sseHeaders });
event.respondWith(response); });
/* * Result / self.addEventListener('fetch', event => { const { headers, url } = event.request; const isSSERequest = headers.get('Accept') === 'text/event-stream';
// Process only SSE connections if (!isSSERequest) { return; }
// Headers for SSE response const sseHeaders = { 'content-type': 'text/event-stream', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', }; // Function for formatting message to SSE response const sseChunkData = (data, event, retry, id) => Object.entries({ event, id, data, retry }) .filter(([, value]) => ![undefined, null].includes(value)) .map(([key, value]) =>
${key}: ${value}
) .join('\n') + '\n\n';// Map with server connections, where key - url, value - EventSource const serverConnections = {}; // For each request opens only one server connection and use it for next requests with the same url const getServerConnection = url => { if (!serverConnections[url]) { serverConnections[url] = new EventSource(url); }
return serverConnections[url];
}; // On message from server forward it to browser const onServerMessage = (controller, { data, type, retry, lastEventId }) => { const responseText = sseChunkData(data, type, retry, lastEventId); const responseData = Uint8Array.from(responseText, x => x.charCodeAt(0)); controller.enqueue(responseData); }; const stream = new ReadableStream({ start: controller => getServerConnection(url).onmessage = onServerMessage.bind(null, controller) }); const response = new Response(stream, { headers: sseHeaders });
event.respondWith(response); }); ```
-
-
Tags
Annotators
URL
-