8,037 Matching Annotations
  1. Dec 2021
    1. $ wget --warc-file eada --mirror --page-requisites --adjust-extension --convert-links --wait 1 --execute robots=off --no-parent http://mith.umd.edu/eada/ > /dev/null 
      WARC output does not work with timestamping, timestamping will be disabled.
      Opening WARC file ‘eada.warc.gz’.
      
      --2021-12-29 17:43:08--  http://mith.umd.edu/eada/
      Resolving mith.umd.edu (mith.umd.edu)... 174.129.6.250
      Connecting to mith.umd.edu (mith.umd.edu)|174.129.6.250|:80... connected.
      HTTP request sent, awaiting response... 301 Moved Permanently
      Location: https://mith.umd.edu/eada/ [following]
      
           0K                                                       100% 25,6M=0s
      
      --2021-12-29 17:43:10--  https://mith.umd.edu/eada/
      Connecting to mith.umd.edu (mith.umd.edu)|174.129.6.250|:443... connected.
      HTTP request sent, awaiting response... 301 Moved Permanently
      Location: https://archive.mith.umd.edu/eada/ [following]
      
           0K                                                       100% 41,1M=0s
      
      --2021-12-29 17:43:11--  https://archive.mith.umd.edu/eada/
      Resolving archive.mith.umd.edu (archive.mith.umd.edu)... 174.129.6.250
      Connecting to archive.mith.umd.edu (archive.mith.umd.edu)|174.129.6.250|:443... connected.
      HTTP request sent, awaiting response... 301 Moved Permanently
      Location: http://eada.lib.umd.edu [following]
      
           0K                                                       100% 42,9M=0s
      
      --2021-12-29 17:43:13--  http://eada.lib.umd.edu/
      Resolving eada.lib.umd.edu (eada.lib.umd.edu)... 129.2.19.174
      Connecting to eada.lib.umd.edu (eada.lib.umd.edu)|129.2.19.174|:80... connected.
      HTTP request sent, awaiting response... 200 OK
      Length: 5210 (5,1K) [text/html]
      Saving to: ‘mith.umd.edu/eada/index.html’
      
           0K .....                                                 100%  447M=0s
      
      2021-12-29 17:43:13 (447 MB/s) - ‘mith.umd.edu/eada/index.html’ saved [5210/5210]
      
      FINISHED --2021-12-29 17:43:13--
      Total wall clock time: 5,2s
      Downloaded: 1 files, 5,1K in 0s (447 MB/s)
      Converting links in mith.umd.edu/eada/index.html... 2-7
      Converted links in 1 files in 0,001 seconds.
      
    1. Removing the navigational toolbar

      [...]

      For example, here is an archived post discussing the id_ identity flag. This is a normal link to the Wayback Machine, which renders with the navigational toolbar:

      Here is the same archived page, with the i<var>d_</var> identity flag added to the link. This does not include the toolbar, but now the page renders poorly because of the broken references:

      Finally, here is the same archived page, with the <var>if_</var> iframe flag instead. This renders perfectly, without the toolbar:

      Since this is the most faithful reproduction of the original web page, please use the <var>if_</var> iframe flag for links to specific archive copies!

    2. Editors are encouraged to add an archive link as a part of each citation, or at least submit the referenced URL for archiving, at the same time that each citation is created or updated. New URLs added to Wikipedia articles (but not other pages) are usually automatically archived by a bot.

      [...]

      In short, this is the code that needs to be added to an existing {{cite web}} or similar template:

      <ref>{{cite ... <!--EXISTING REFERENCE--> |archive-url=https://web.archive.org/web/<date>/http://www.originalurl.com |archive-date=<date> |url-status=dead}}</ref>
      
    1. WET Response Format

      As many tasks only require textual information, the CommonCrawl dataset provides WET files that only contain extracted plaintext. The way in which this textual data is stored in the WET format is quite simple. The WARC metadata contains various details, including the URL and the length of the plaintext data, with the plaintext data following immediately afterwards.

      WARC/1.0
      WARC-Type: conversion
      WARC-Target-URI: http://advocatehealth.com/condell/emergencyservices3
      WARC-Date: 2013-12-04T15:30:35Z
      WARC-Record-ID: 
      WARC-Refers-To: 
      WARC-Block-Digest: sha1:3SJBHMFPOCUJEHJ7OMGVCRSHQTWLJUUS
      Content-Type: text/plain
      Content-Length: 5765
      
      
      ...Text Content...
      
    2. WAT Response Format

      WAT files contain important metadata about the records stored in the WARC format above. This metadata is computed for each of the three types of records (metadata, request, and response). If the information crawled is HTML, the computed metadata includes the HTTP headers returned and the links (including the type of link) listed on the page.

      This information is stored as JSON. To keep the file sizes as small as possible, the JSON is stored with all unnecessary whitespace stripped, resulting in a relatively unreadable format for humans. If you want to inspect the JSON file yourself, use one of the many JSON pretty print tools available.

      The HTTP response metadata is most likely to be of interest to CommonCrawl users. The skeleton of the JSON format is outlined below.

          Envelope
              WARC-Header-Metadata
              Payload-Metadata
                  HTTP-Response-Metadata
                      Headers
                          HTML-Metadata
                              Head
                                  Title
                                  Scripts
                                  Metas
                                  Links
                              Links
              Container
      
    3. WARC Format

      The WARC format is the raw data from the crawl, providing a direct mapping to the crawl process. Not only does the format store the HTTP response from the websites it contacts (WARC-Type: response), it also stores information about how that information was requested (WARC-Type: request) and metadata on the crawl process itself (WARC-Type: metadata).

      For the HTTP responses themselves, the raw response is stored. This not only includes the response itself, what you would get if you downloaded the file, but also the HTTP header information, which can be used to glean a number of interesting insights.

      In the example below, we can see the crawler contacted http://102jamzorlando.cbslocal.com/tag/nba/page/2/ and received a HTML page in response. We can also see the page was served from the nginx web server and that a special header has been added, X-hacker, purely for the purposes of advertising to a very specific audience of programmers who might look at the HTTP headers!

      WARC/1.0
      WARC-Type: response
      WARC-Date: 2013-12-04T16:47:32Z
      WARC-Record-ID: 
      Content-Length: 73873
      Content-Type: application/http; msgtype=response
      WARC-Warcinfo-ID: 
      WARC-Concurrent-To: 
      WARC-IP-Address: 23.0.160.82
      WARC-Target-URI: http://102jamzorlando.cbslocal.com/tag/nba/page/2/
      WARC-Payload-Digest: sha1:FXV2BZKHT6SQ4RZWNMIMP7KMFUNZMZFB
      WARC-Block-Digest: sha1:GMYFZYSACNBEGHVP3YFQNOSTV5LPXNAU
      
      HTTP/1.0 200 OK
      Server: nginx
      Content-Type: text/html; charset=UTF-8
      Vary: Accept-Encoding
      Vary: Cookie
      X-hacker: If you're reading this, you should visit automattic.com/jobs and apply to join the fun, mention this header.
      Content-Encoding: gzip
      Date: Wed, 04 Dec 2013 16:47:32 GMT
      Content-Length: 18953
      Connection: close
      
      
      ...HTML Content...
      
    1. Currently starting to read https://github.com/substack/stream-handbook, maybe I just need a deeper understanding of streams

      According to @xander76 on Twitter the code would have to use a Transform stream, which looks something like this:

      let cacheEntry = "";
      renderToNodeStream(<Frontend/>)
        .pipe(new Transform({
          transform(chunk, enc, callback) {
            cacheEntry += chunk; callback(chunk);
          },
          flush(callback) {
            redis.set(req.path, cacheEntry);
          }
        })
        .pipe(res);
      
    1. 
      //getDates.js
      
      // Returns an array of dates between the two dates
      function getDates (startDate, endDate) {
        const dates = []
        let currentDate = startDate
        const addDays = function (days) {
          const date = new Date(this.valueOf())
          date.setDate(date.getDate() + days)
          return date
        }
        while (currentDate <= endDate) {
          dates.push(currentDate)
          currentDate = addDays.call(currentDate, 1)
        }
        return dates
      }
      
      // Usage
      const dates = getDates(new Date(2013, 10, 22), new Date(2013, 11, 25))
      dates.forEach(function (date) {
        console.log(date)
      })
      
    1. Abstract: [...] [The HTTP Memento protocol] lacks support for the management of data updated at high frequencies or the interactions during resource modification and only provides inefficient means for managing resources with many revisions. To address these shortcomings, we propose three extensions to the HTTP Memento protocol: arbitrary resolution timestamps, resource creation support and range requests for TimeMaps. We provide a reference implementation of our proposals as open source software and quantitatively evaluate the extensions’ performance, showcasing superior results in terms of resource capacity, insertion correctness, latency and amount of transferred data.

    1. npm init -y \
      && npm i --save-dev node@16 \
      && npm config set prefix=$(pwd)/node_modules/node \
      && export PATH=$(pwd)/node_modules/node/bin:$PATH
      
    1. .container {
        width: 100%;
        height: 100vh;
        overflow: scroll;
        -webkit-overflow-scrolling: touch;
        scroll-snap-type: y mandatory;
      }
      
      .item {
        position: sticky;
        position: -webkit-sticky;
        top: 0;
        scroll-snap-align: start;
        width: 100%;
        height: 100vh;   
      }
      

      Demo

    1. function getDomPath(el) {
        var stack = [];
        while ( el.parentNode != null ) {
          console.log(el.nodeName);
          var sibCount = 0;
          var sibIndex = 0;
          for ( var i = 0; i < el.parentNode.childNodes.length; i++ ) {
            var sib = el.parentNode.childNodes[i];
            if ( sib.nodeName == el.nodeName ) {
              if ( sib === el ) {
                sibIndex = sibCount;
              }
              sibCount++;
            }
          }
          if ( el.hasAttribute('id') && el.id != '' ) {
            stack.unshift(el.nodeName.toLowerCase() + '#' + el.id);
          } else if ( sibCount > 1 ) {
            stack.unshift(el.nodeName.toLowerCase() + ':eq(' + sibIndex + ')');
          } else {
            stack.unshift(el.nodeName.toLowerCase());
          }
          el = el.parentNode;
        }
        return stack.slice(1); // removes the html element
      }
      
      //Usage:
      var path = getDomPath(document.getElementById('button'));
      console.log(path.join(' > '));
      
    1. The Annodex annotation format for time-continuous bitstreams, Version 2.0 draft-pfeiffer-annodex-01

      Abstract

      This specification defines a file format for annotating and indexing time-continuous bitstreams for the World Wide Web. The format has been named "Annodex" for annotating and indexing. The Annodex format enables the specification of named anchor points in time-continuous bitstreams together with textual annotations and hyperlinks in URI [4] format. These anchor points are merged time-synchronously with the time-continuous bitstreams when authoring a file in Annodex format. The ultimate aim of the Annodex format is to enable an integration of time-continous bitstreams into the browsing and searching functionality of the World Wide Web.

    1. JCDL 2010 presentation about using Memento to reconstruct the state for web resources involved in annotation.
    1. {
        "@context": {
          "oa": "http://www.w3.org/ns/oa#",
          "dc": "http://purl.org/dc/elements/1.1/",
          "dcterms": "http://purl.org/dc/terms/",
          "dctypes": "http://purl.org/dc/dcmitype/",
          "foaf": "http://xmlns.com/foaf/0.1/",
          "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
          "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
          "skos": "http://www.w3.org/2004/02/skos/core#",
          "text": {
            "@id": "oa:hasBody"
          },
          "target": {
            "@type": "@id",
            "@id": "oa:hasTarget"
          },
          "source": {
            "@type": "@id",
            "@id": "oa:hasSource"
          },
          "selector": {
            "@type": "@id",
            "@id": "oa:hasSelector"
          },
          "state": {
            "@type": "@id",
            "@id": "oa:hasState"
          },
          "scope": {
            "@type": "@id",
            "@id": "oa:hasScope"
          },
          "user": {
            "@type": "@id",
            "@id": "oa:annotatedBy"
          },
          "serializedBy": {
            "@type": "@id",
            "@id": "oa:serializedBy"
          },
          "motivation": {
            "@type": "@id",
            "@id": "oa:motivatedBy"
          },
          "stylesheet": {
            "@type": "@id",
            "@id": "oa:styledBy"
          },
          "cached": {
            "@type": "@id",
            "@id": "oa:cachedSource"
          },
          "conformsTo": {
            "@type": "@id",
            "@id": "dcterms:conformsTo"
          },
          "members": {
            "@type": "@id",
            "@id": "oa:membershipList",
            "@container": "@list"
          },
          "item": {
            "@type": "@id",
            "@id": "oa:item"
          },
          "related": {
            "@type": "@id",
            "@id": "skos:related"
          },
          "format": "dc:format",
          "language": "dc:language",
          "created": "oa:annotatedAt",
          "updated": "oa:serializedAt",
          "when": "oa:when",
          "value": "rdf:value",
          "start": "oa:start",
          "end": "oa:end",
          "exact": "oa:exact",
          "prefix": "oa:prefix",
          "suffix": "oa:suffix",
          "label": "rdfs:label",
          "name": "foaf:name",
          "mbox": "foaf:mbox",
          "nick": "foaf:nick",
          "styleClass": "oa:styleClass",
          "@base": "http://hypothes.is/api/annotations/",
          "id": "@id",
          "tags": "oa:Tag"
        },
        "updated": "2014-09-18T21:43:16.353744+00:00",
        "target": [
          {
            "source": "http://faculty.georgetown.edu/irvinem/theory/Berners-Lee-HTTP-proposal.pdf",
            "pos": {
              "top": 549.5,
              "height": 17
            },
            "selector": [
              {
                "type": "RangeSelector",
                "startContainer": "/div[1]/div[2]/div[4]/div[1]/div[1]/div[2]/div[16]",
                "endContainer": "/div[1]/div[2]/div[4]/div[1]/div[1]/div[2]/div[16]",
                "startOffset": 0,
                "endOffset": 7
              },
              {
                "start": 397,
                "end": 404,
                "type": "TextPositionSelector"
              },
              {
                "type": "TextQuoteSelector",
                "prefix": "information Hypermedia CERNDOC",
                "exact": "ENQUIRE",
                "suffix": "Tim Berners-Lee section group C"
              }
            ]
          }
        ],
        "created": "2014-09-18T21:32:13.492351+00:00",
        "text": "As featured in \"Weaving the Web\" by Tim Berners-Lee",
        "tags": [
          "web",
          "history"
        ],
        "uri": "http://faculty.georgetown.edu/irvinem/theory/Berners-Lee-HTTP-proposal.pdf",
        "user": "acct:BigBlueHat@hypothes.is",
        "document": {
          "eprints": {},
          "title": "Berners-Lee-HTTP-proposal.pdf",
          "twitter": {},
          "dc": {},
          "prism": {},
          "highwire": {},
          "facebook": {},
          "reply_to": [],
          "link": [
            {
              "href": "http://faculty.georgetown.edu/irvinem/theory/Berners-Lee-HTTP-proposal.pdf"
            }
          ]
        },
        "consumer": "00000000-0000-0000-0000-000000000000",
        "id": "Gk_TW9d_SyCG5cFH4UCy9A",
        "permissions": {
          "admin": [
            "acct:BigBlueHat@hypothes.is"
          ],
          "read": [
            "acct:BigBlueHat@hypothes.is",
            "group:__world__"
          ],
          "update": [
            "acct:BigBlueHat@hypothes.is"
          ],
          "delete": [
            "acct:BigBlueHat@hypothes.is"
          ]
        }
      }
      
    1. Using Web Workers

      You can run highlighting inside a web worker to avoid freezing the browser window while dealing with very big chunks of code.

      // In your main script:
      addEventListener('load', () => {
        const code = document.querySelector('#code');
        const worker = new Worker('worker.js');
        worker.onmessage = ({data}) => { code.innerHTML = data; }
        worker.postMessage(code.textContent);
      });
      
      // In worker.js:
      
      onmessage = (event) => {
        importScripts('<path>/highlight.min.js');
        const result = self.hljs.highlightAuto(event.data);
        postMessage(result.value);
      };
      
    1. API

      In order to access the JavaScript object which provides the SoundCloud Widget API, add this script to your html page.

      This script exposes the SC.Widget(/*iframeElement|iframeElementID*/) function to the global scope. It allows you to control the widget from the parent page (the page the widget is inserted into). SC.Widget accepts the reference to the iframe element or its id.

      var iframeElement   = document.querySelector('iframe');
      var iframeElementID = iframeElement.id;
      var widget1         = SC.Widget(iframeElement);
      var widget2         = SC.Widget(iframeElementID);
      // widget1 === widget2
      
    1. Under-the-hood working of the streaming SSR server with the new React 14's suspense. A thread. #reactjs #webperf #perfmatters
    1. Stick to the bottom?!

      [...]

      But you can also use it to stick elements to the bottom. That means that the footer can be defined to have a sticky position, and it will always appear to stick to the bottom when scrolling down. When we reach the end of the sticky container, the element will stop in its natural position. It’s better to use it on elements whose natural position is the bottom of the sticky container.

      Full Example:

      • HTML
        <main class="main-container">
        <header class="main-header">HEADER</header>
        <div class="main-content">MAIN CONTENT</div>
        <footer class="main-footer">FOOTER</footer>
        </main>
        
      • CSS
        .main-footer{     
           position: sticky; 
           bottom: 0;
        }
        
        Live CodePen Example
    1. What are the Contents of a BLOB?

      In order to view, how data is stored in a blob, we will use the readAsDataURL method of FileReader API in JavaScript. The result attribute contains the data as a data: URL representing the file's data as a base64 encoded string.

      var myBlob = new Blob(['<html><h2>This is heading</h2></html>'], {type : 'text/html'});
      
      var reader = new FileReader();
      
      // This fires after the blob has been read
      reader.addEventListener('loadend', (e) => {
        var text = e.target.result;
        console.log("Contents : "+ text);
      });
      
      // Start reading the contents of blob
      reader.readAsDataURL(myBlob);
      

      The output in the console :

      Contents : data:text/html;base64,PGh0bWw+PGgyPlRoaXMgaXMgaGVhZGluZzwvaDI+PC9odG1sPg==

      The text contained in our file is base64-encoded. We can try extracting the encoded part and decode it using the method atob() to check that it is the same.

       // Extracting it from the character at index=22 only in this case as we
        // already know the contents of our Blob
        var extractText = text.slice(22);
        console.log("Decoded "+ atob(extractText));
        // Output : Decoded <html><h2>This is heading</h2></html>
      
    1. Adding flexibility to our contrast checker function

      Brian’s function only accepts six-character hexcolors, and they cannot have a leading hash (#).

      Let’s first modify it to accept a leading hash. We’ll use Array.slice() to get the first character and check if it equals #. If it does, we’ll use Array.slice() again to remove the leading hash and redefine hexcolor.

      Next, let’s modify the function to allow both three and six-character colors.

      To do that, we’ll first check the length of hexcolor. If it’s 3, we’ll use Array.split() to convert the hexcolor string into an array of characters. Then, we’ll use Array.map() to double each character, and Array.join() to combine it back into a string.

      /*!
       * Get the contrasting color for any hex color
       * (c) 2019 Chris Ferdinandi, MIT License, https://gomakethings.com
       * Derived from work by Brian Suda, https://24ways.org/2010/calculating-color-contrast/
       * @param  {String} A hexcolor value
       * @return {String} The contrasting color (black or white)
       */
      var getContrast = function (hexcolor){
      
          // If a leading # is provided, remove it
          if (hexcolor.slice(0, 1) === '#') {
              hexcolor = hexcolor.slice(1);
          }
      
          // If a three-character hexcode, make six-character
          if (hexcolor.length === 3) {
              hexcolor = hexcolor.split('').map(function (hex) {
                  return hex + hex;
              }).join('');
          }
      
          // Convert to RGB value
          var r = parseInt(hexcolor.substr(0,2),16);
          var g = parseInt(hexcolor.substr(2,2),16);
          var b = parseInt(hexcolor.substr(4,2),16);
      
          // Get YIQ ratio
          var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
      
          // Check contrast
          return (yiq >= 128) ? 'black' : 'white';
      
      };
      

      Here’s a demo on CodePen

    1. The second equation is called ‘YIQ’ because it converts the RGB color space into YIQ, which takes into account the different impacts of its constituent parts. Again, the equation returns white or black and it’s also very easy to implement.

      In JavaScript:

      function getContrastYIQ(hexcolor){
          var r = parseInt(hexcolor.substr(0,2),16);
          var g = parseInt(hexcolor.substr(2,2),16);
          var b = parseInt(hexcolor.substr(4,2),16);
          var yiq = ((r*299)+(g*587)+(b*114))/1000;
          return (yiq >= 128) ? 'black' : 'white';
      }
      

      You’ll notice first that we have broken down the hex value into separate RGB values. This is important because each of these channels is scaled in accordance to its visual impact. Once everything is scaled and normalized, it will be in a range between zero and 255. Much like the previous ’50%’ function, we now need to check if the input is above or below halfway. Depending on where that value is, we’ll return the corresponding highest contrasting color.

    2. There are two functions I want to compare.

      The first, I call ’50%’. It takes the hex value and compares it to the value halfway between pure black and pure white. If the hex value is less than half, meaning it is on the darker side of the spectrum, it returns white as the text color. If the result is greater than half, it’s on the lighter side of the spectrum and returns black as the text value.

      In JavaScript:

      function getContrast50(hexcolor){
          return (parseInt(hexcolor, 16) > 0xffffff/2) ? 'black':'white';
      }
      

      It doesn’t get much simpler than that! The function converts the six-character hex color into an integer and compares that to one half the integer value of pure white. The function is easy to remember, but is naive when it comes to understanding how we perceive parts of the spectrum. Different wavelengths have greater or lesser impact on the contrast.

    1. If you want to define a theme color for light and dark mode, your best bet is to define both colors and use the first meta tag as a fallback for browsers that don’t support the media feature.

      <meta name="theme-color" content="#319197" media="(prefers-color-scheme: light)">
      <meta name="theme-color" content="#872e4e" media="(prefers-color-scheme: dark)">
      
    1. This example implements text copying when a data-copy attribute is added any HTML element such as a button. You can set this value to either of these:

      1. a hard-coded string — such as data-copy="copy this to the clipboard".
      2. a CSS selector — such as data-copy="#mysection". The text content of the first matching element is then copied.

      Optionally, you can set a custom success message in a data-done attribute:

      <button data-copy="#mysection" data-done="section copied">
        copy text from #mysection
      </button>
      

      The button is only shown when navigator.clipboard.writeText() is supported. When clicked, the JavaScript event handler locates the text, copies it to the clipboard, and shows an animated success message.

      The text paste button is very similar except it defines a data-paste attribute which must point to a DOM node:

      <textarea id="pastehere"></textarea>
      <button data-paste="#pastehere">paste</button>
      
    2. Copy and Paste Data

      The Clipboard API’s readText() and writeText() are convenience options for the more generic read() and write() methods. These have less browser support but are able to copy and paste any type of data such as binary images.

      Copying requires blob data typically returned by a fetch() or canvas.toBlob() method. This is passed to a ClipboardItem constructor so it can be written to the clipboard:

      const
        image = await fetch('myimage.png'),
        blob = await image.blob();
      
      await navigator.clipboard.write([
        new ClipboardItem({ [blob.type]: blob })
      ]);
      

      Pasting is more complex because multiple ClipboardItem objects can be returned with differing content types. It’s therefore necessary to iterate through each type until a useful format is found. For example:

      const clipboardItems = await navigator.clipboard.read();
      
      for (const clipboardItem of clipboardItems) {
        for (const type of clipboardItem.types) {
          if (type === 'image/png') {
            // return PNG blob
            return await clipboardItem.getType(type);
          }
        }
      }
      
    3. Cut, Copy, and Paste Events

      The cut, copy, and paste events fire whenever the user initiates a clipboard action in the browser — typically with right-click menus or the keyboard shortcuts mentioned above. This is supported in most browsers and handler functions can intercept the events to make changes using a `clipboardData object* passed as a parameter.

      The following function forces all cut or copied text to be uppercase. Note that e.preventDefault() stops the default cut/copy action which would override it:

      body.addEventListener('cut', cutCopyHandler);
      body.addEventListener('copy', cutCopyHandler);
      
      // cut or copy event handler
      function cutCopyHandler(e) {
      
        const selection = document.getSelection();
      
        // send uppercase text to clipboard
        e.clipboardData.setData(
          'text/plain',
          selection.toString().toUpperCase()
        );
      
        if (e.type === 'cut') selection.deleteFromDocument();
      
        // stop default cut/copy
        e.preventDefault();
      
      }
      

      The following code attaches a paste handler to a specific <textarea> field. The function clears the existing content and prefixes the text "pasted:":

      document.getElementById('field1').addEventListener('paste', pasteEvent);
      
      // paste event handler
      function pasteEvent(e) {
      
        // add 'pasted:' to pasted text
        const paste = 'pasted:\n' +
          (e.clipboardData || window.clipboardData).getData('text');
      
        e.target.value = paste;
      
        // stop default paste
        e.preventDefault();
      }
      
    4. This works in a similar way to the text demonstration, in that copy and paste buttons must point to DOM elements using a CSS selector in data-copyblob and data-pasteblob attributes. For example:

      <!-- copy image -->
      <img id="myimage" src="myimage.png" alt="any image" />
      
      <button data-copyblob="#myimage" data-done="image copied">
        copy image
      </button>
      
      <!-- paste into DOM -->
      <div id="imagelist"></div>
      
      <button data-pasteblob="#imagelist">
        paste image
      </button>
      

      Try copying image data from a graphics application, then use the paste button.

    1. If your site has multiple URLs for the same content, share the page's canonical URL instead of the current URL. Instead of sharing document.location.href, you would check for a canonical URL <meta> tag in the page's <head> and share that. This will provide a better experience to the user. Not only does it avoid redirects, but it also ensures that a shared URL serves the correct user experience for a particular client. For example, if a friend shares a mobile URL and you look at it on a desktop computer, you should see a desktop version:

      let url = document.location.href;
      const canonicalElement = document.querySelector('link[rel=canonical]');
      if (canonicalElement !== null) {
          url = canonicalElement.href;
      }
      navigator.share({url});
      
    1. Here are the single characters which can be normalised down to a valid TLD. They're mostly country codes, but there are a few interesting exceptions:

      ㏕ - US Military
      ℡ - .tel registry
      № - Norway
      ㍳ - Australia
      ㍷ - Dominica
      ㎀ - Panama
      ㎁ - Namibia
      ㎃ - Morocco
      ㎊ - French Polynesia
      ㎋ - Norfolk Island
      ㎏ - Kyrgyzstan
      ㎖ - Mali
      ㎙ - Federated States of Micronesia
      fi - Finland
      ㎜ - Myanmar
      ㎝ - Cameroon
      ㎞ & ㏎ - Comoros
      ㎰ - Palestine
      ㎳ - Montserrat
      ㎷ & ㎹ - Republic of Maldives.
      ㎺ - Palau
      ㎽ & ㎿ - Malawi
      ㏄ - Cocos (Keeling) Islands
      ㏅ - Democratic Republic of Congo
      ㏉ - Guyana
      ㏗ - Philippines
      ㏘ - Saint Pierre and Miquelon
      ㏚ - Puerto Rico
      ㏛ - Suriname
      ㏜ - El Salvador
      ℠ - San Marino
      ™ - Turkmenistan
      st & ſt - São Tomé and Príncipe
      ㎇ - Great Britain (Obsolete)
      ß - South Sudan (Not available)
      ㏌ - India and Indiana (subdomain of .us)
      Ⅵ & ⅵ - Virgin Islands and Virginia (subdomain of .us)
      fl - Florida (subdomain of .us)
      ㎚ - New Mexico (subdomain of .us)
      ㎵ - Nevada (subdomain of .us)
      ㍵ - As part of .ovh
      
    2. Nestling among the "Letterlike Symbols" are two curious entries. Both of these are single characters:

      • Telephone symbol - ℡
      • Numero Sign - №

      What's interesting is both .tel and .no are Top-Level-Domains (TLD) on the Domain Name System (DNS).

      So my contact site - https://edent.tel/ - can be written as - https://edent.℡/

      And the Norwegian domain name registry NORID can be accessed at https://www.norid.№/

      Copy and paste those links - they work in any browser!

    1. /* ScrollToTop.js */
      
      import { useEffect } from 'react';
      import { withRouter } from 'react-router-dom';
      
      function ScrollToTop({ history }) {
        useEffect(() => {
          const unlisten = history.listen(() => {
            window.scrollTo(0, 0);
          });
          return () => {
            unlisten();
          }
        }, []);
      
        return (null);
      }
      
      export default withRouter(ScrollToTop);
      
    1. dokieli is a clientside editor for decentralised article publishing, annotations and social interactions.
    1. Transparent data access

      JavaScript Proxies enable LDflex processors to translate path expressions to SPARQL queries and resolve them through a query engine (e.g. Comunica)

      await [https://julianrojas.org/#me].name
      
      SELECT ?name WHERE {
        <https://julianrojas.org/#me> <http://xmlns.com/foaf/0.1/name> ?name.
      }
      
    2. Support for data writing

      LDflex allows for writing/updating knowledge graphs that support SPARQL UPDATE operations such as SPARQL endpoints and Solid pods.

      For example this expression:

      await [https://julianrojas.solid.org/profile/#me].add('foaf:givenName' , 'Julian');
      

      Will be translated to this SPARQL UPDATE query:

      INSERT DATA {
        <https://julianrojas.solid.org/profile/#me> 
          <http://xmlns.com/foaf/0.1/givenName> "Julian".
      }
      
    3. How does it work? Thanks to JSON-LD contexts and JavaScript Proxies we can treat Linked Data graphs as local objects The await keyword allows waiting for remote HTTP requests
    4. Traverse Linked Data as JS objects

      await [https://julianrojas.org/#me].name // "Julián Rojas"
      await [https://julianrojas.org/#me].friends.name // ["Ruben Verborgh", "Ruben Taelman", ...]
      
  2. datatracker.ietf.org datatracker.ietf.org
    1. 6.1. Link Relation Type: service-doc

      Relation Name: service-doc

      Description: Identifies service documentation for the context that is primarily intended for human consumption.

      6.2. Link Relation Type: service-desc

      Relation Name: service-desc

      Description: Identifies service description for the context that is primarily intended for consumption by machines.

      6.3. Link Relation Type: service-meta

      Relation Name: service-meta

      Description: Identifies general metadata for the context that is primarily intended for consumption by machines.

      6.4. Link Relation Type: status

      Relation Name: status

      Description: Identifies a resource that represents the context's status.

    1. Expialidocio.us is a tool for visualizing your del.icio.us posting activity. It displays a graph of your posting activity over time. You can select a timespan from this graph, and it will show you a tag cloud weighted by just those dates. Expialidocio.us was inspired by a posting by Jon Udell. Coming full circle, Udell has posted since posted about this application. Since then, I’ve published the sources.
    1. extispicious, a. [L. extispicium an inspection of the innards for divination; extra the entrails + specer to look at.] Relating to the inspection of entrails for prognostication.
    1. <table> <thead><tr> <th>Type Name</th> <th>Icon Name</th> <th>Icon Page</th> </tr> </thead> <tbody> <tr> <td>Thing</td> <td>information</td> <td>http://thenounproject.com/noun/information/#icon-No22</td> </tr> <tr> <td>Creative Work</td> <td>book</td> <td>http://thenounproject.com/noun/book/#icon-No2248</td> </tr> <tr> <td>Article</td> <td>paper</td> <td>http://thenounproject.com/noun/paper/#icon-No1313</td> </tr> <tr> <td>Blog</td> <td>blog</td> <td>http://thenounproject.com/noun/blog/#icon-No328</td> </tr> <tr> <td>Book</td> <td>book</td> <td>http://thenounproject.com/noun/book/#icon-No2248</td> </tr> <tr> <td>Comment</td> <td>blog</td> <td>http://thenounproject.com/noun/blog/#icon-No460</td> </tr> <tr> <td>ItemList</td> <td>notebook</td> <td>http://thenounproject.com/noun/notebook/#icon-No1604</td> </tr> <tr> <td>Map</td> <td>map</td> <td>http://thenounproject.com/noun/map/#icon-No1670</td> </tr> <tr> <td>MediaObject</td> <td>media</td> <td>http://thenounproject.com/noun/media/#icon-No1565</td> </tr> <tr> <td>Movie</td> <td>movie</td> <td>http://thenounproject.com/noun/movie/#icon-No1683</td> </tr> <tr> <td>MusicPlaylist</td> <td>ipod</td> <td>http://thenounproject.com/noun/ipod/#icon-No2169</td> </tr> <tr> <td>MusicRecording</td> <td>compact disc</td> <td>http://thenounproject.com/noun/compact-disc/#icon-No1231</td> </tr> <tr> <td>Painting</td> <td>paint palette</td> <td>http://thenounproject.com/noun/paint-palette/#icon-No1435</td> </tr> <tr> <td>Photograph</td> <td>polaroid</td> <td>http://thenounproject.com/noun/polaroid/#icon-No1735</td> </tr> <tr> <td>Recipe</td> <td>oven</td> <td>http://thenounproject.com/noun/oven/#icon-No2244</td> </tr> <tr> <td>Review</td> <td>ribbon</td> <td>http://thenounproject.com/noun/ribbon/#icon-No1317</td> </tr> <tr> <td>Sculpture</td> <td></td> <td></td> </tr> <tr> <td>SoftwareApplication</td> <td></td> <td></td> </tr> <tr> <td>TVEpisode</td> <td>television</td> <td>http://thenounproject.com/noun/television/#icon-No2315</td> </tr> <tr> <td>TVSeason</td> <td>television</td> <td>http://thenounproject.com/noun/television/#icon-No2315</td> </tr> <tr> <td>TVSeries</td> <td>television</td> <td>http://thenounproject.com/noun/television/#icon-No2315</td> </tr> <tr> <td>WebPage</td> <td>document</td> <td>http://thenounproject.com/noun/document/#icon-No453</td> </tr> <tr> <td>WebPageElement</td> <td>document</td> <td>http://thenounproject.com/noun/document/#icon-No453</td> </tr> <tr> <td>Event</td> <td>calendar</td> <td>http://thenounproject.com/noun/calendar/#icon-No404</td> </tr> <tr> <td>Intangible</td> <td></td> <td></td> </tr> <tr> <td>Organization</td> <td>network</td> <td>http://thenounproject.com/noun/network#icon-No1056</td> </tr> <tr> <td>Corporation</td> <td>meeting</td> <td>http://thenounproject.com/noun/meeting/#icon-No1173</td> </tr> <tr> <td>EducationalOrganization</td> <td>school</td> <td>http://thenounproject.com/noun/school/#icon-No2455</td> </tr> <tr> <td>CollegeOrUniversity</td> <td>college</td> <td>http://thenounproject.com/noun/college/#icon-No2402</td> </tr> <tr> <td>ElementarySchool</td> <td></td> <td></td> </tr> <tr> <td>HighSchool</td> <td></td> <td></td> </tr> <tr> <td>MiddleSchool</td> <td></td> <td></td> </tr> <tr> <td>Preschool</td> <td>school</td> <td>http://thenounproject.com/noun/school/#icon-No2401</td> </tr> <tr> <td>School</td> <td>school</td> <td>http://thenounproject.com/noun/school/#icon-No2455</td> </tr> <tr> <td>GovernmentOrganization</td> <td>institution</td> <td>http://thenounproject.com/noun/institution/#icon-No1564</td> </tr> <tr> <td>LocalBusiness</td> <td>building</td> <td>http://thenounproject.com/noun/building/#icon-No1808</td> </tr> <tr> <td>AnimalShelter</td> <td></td> <td></td> </tr> <tr> <td>AutomotiveBusiness</td> <td>car service</td> <td>http://thenounproject.com/noun/car-service/#icon-No1705</td> </tr> <tr> <td>ChildCare</td> <td></td> <td></td> </tr> <tr> <td>DryCleaningOrLaundry</td> <td></td> <td></td> </tr> <tr> <td>EmergencyService</td> <td></td> <td></td> </tr> <tr> <td>EmploymentAgency</td> <td></td> <td></td> </tr> <tr> <td>EntertainmentBusiness</td> <td>theater</td> <td>http://thenounproject.com/noun/theater/#icon-No1554</td> </tr> <tr> <td>FinancialService</td> <td>bank</td> <td>http://thenounproject.com/noun/bank/#icon-No4</td> </tr> <tr> <td>FoodEstablishment</td> <td>restaurant</td> <td>http://thenounproject.com/noun/restaurant/#icon-No24</td> </tr> <tr> <td>GovernmentOffice</td> <td>town hall</td> <td>http://thenounproject.com/noun/town-hall/#icon-No2406</td> </tr> <tr> <td>HealthAndBeautyBusiness</td> <td>beauty salon</td> <td>http://thenounproject.com/noun/beauty-salon/#icon-No15</td> </tr> <tr> <td>HomeAndConstructionBusiness</td> <td></td> <td></td> </tr> <tr> <td>InternetCafe</td> <td></td> <td></td> </tr> <tr> <td>Library</td> <td>library</td> <td>http://thenounproject.com/noun/library/#icon-No191</td> </tr> <tr> <td>LodgingBusiness</td> <td>hotel</td> <td>http://thenounproject.com/noun/hotel/#icon-No46</td> </tr> <tr> <td>MedicalOrganization</td> <td>hospital</td> <td>http://thenounproject.com/noun/hospital/#icon-No1658</td> </tr> <tr> <td>ProfessionalService</td> <td></td> <td></td> </tr> <tr> <td>RadioStation</td> <td>radio tower</td> <td>http://thenounproject.com/noun/radio-tower/#icon-No1316</td> </tr> <tr> <td>RealEstateAgent</td> <td></td> <td></td> </tr> <tr> <td>RecyclingCenter</td> <td>recycle</td> <td>http://thenounproject.com/noun/recycle/#icon-No60</td> </tr> <tr> <td>SelfStorage</td> <td></td> <td></td> </tr> <tr> <td>ShoppingCenter</td> <td>shops</td> <td>http://thenounproject.com/noun/shops/#icon-No28</td> </tr> <tr> <td>SportsActivityLocation</td> <td>gymnasium</td> <td>http://thenounproject.com/noun/gymnasium/#icon-No736</td> </tr> <tr> <td>Store</td> <td>grocery store</td> <td>http://thenounproject.com/noun/grocery-store/#icon-No2386</td> </tr> <tr> <td>TelevisionStation</td> <td></td> <td></td> </tr> <tr> <td>TouristInformationCenter</td> <td>information</td> <td>http://thenounproject.com/noun/information/#icon-No908</td> </tr> <tr> <td>TravelAgency</td> <td></td> <td></td> </tr> <tr> <td>NGO</td> <td></td> <td></td> </tr> <tr> <td>PerformingGroup</td> <td>team</td> <td>http://thenounproject.com/noun/team/#icon-No1016</td> </tr> <tr> <td>SportsTeam</td> <td></td> <td></td> </tr> <tr> <td>Person</td> <td>man</td> <td>http://thenounproject.com/noun/man/#icon-No2</td> </tr> <tr> <td>Place</td> <td>Map-marker</td> <td>http://thenounproject.com/noun/map-marker/#icon-No462</td> </tr> <tr> <td>AdministrativeArea</td> <td>city</td> <td>http://thenounproject.com/noun/city/#icon-No1566</td> </tr> <tr> <td>City</td> <td>city</td> <td>http://thenounproject.com/noun/city/#icon-No1566</td> </tr> <tr> <td>Country</td> <td></td> <td></td> </tr> <tr> <td>State</td> <td>texas</td> <td>http://thenounproject.com/noun/texas/#icon-No2286</td> </tr> <tr> <td>CivicStructure</td> <td>town hall</td> <td>http://thenounproject.com/noun/town-hall/#icon-No2406</td> </tr> <tr> <td>Airport</td> <td>airport</td> <td>http://thenounproject.com/noun/airport/#icon-No2431</td> </tr> <tr> <td>Aquarium</td> <td></td> <td></td> </tr> <tr> <td>Beach</td> <td>beach chair</td> <td>http://thenounproject.com/noun/beach-chair/#icon-No1611</td> </tr> <tr> <td>BusStation</td> <td>bus stop</td> <td>http://thenounproject.com/noun/bus-stop/#icon-No2246</td> </tr> <tr> <td>BusStop</td> <td>bus stop</td> <td>http://thenounproject.com/noun/bus-stop/#icon-No2246</td> </tr> <tr> <td>Campground</td> <td>campground</td> <td>http://thenounproject.com/noun/campground/#icon-No209</td> </tr> <tr> <td>Cemetery</td> <td>cemetery</td> <td>http://thenounproject.com/noun/cemetery/#icon-No2441</td> </tr> <tr> <td>Crematorium</td> <td>cemetery</td> <td>http://thenounproject.com/noun/cemetery/#icon-No2441</td> </tr> <tr> <td>EventVenue</td> <td>theater</td> <td>http://thenounproject.com/noun/theater/#icon-No501</td> </tr> <tr> <td>FireStation</td> <td>fire station</td> <td>http://thenounproject.com/noun/fire-station/#icon-No2405</td> </tr> <tr> <td>GovernmentBuilding</td> <td>capital</td> <td>http://thenounproject.com/noun/capital/#icon-No1761</td> </tr> <tr> <td>Hospital</td> <td>hospital</td> <td>http://thenounproject.com/noun/hospital/#icon-No2384</td> </tr> <tr> <td>MovieTheater</td> <td>cinema</td> <td>http://thenounproject.com/noun/cinema/#icon-No313</td> </tr> <tr> <td>Museum</td> <td>museum</td> <td>http://thenounproject.com/noun/museum/#icon-No2428</td> </tr> <tr> <td>MusicVenue</td> <td>theater</td> <td>http://thenounproject.com/noun/theater/#icon-No501</td> </tr> <tr> <td>Park</td> <td>park</td> <td>http://thenounproject.com/noun/picnic-table/#icon-No208</td> </tr> <tr> <td>ParkingFacility</td> <td>parking</td> <td>http://thenounproject.com/noun/parking/#icon-No27</td> </tr> <tr> <td>PerformingArtsTheater</td> <td>theater</td> <td>http://thenounproject.com/noun/theater/#icon-No1548</td> </tr> <tr> <td>PlaceOfWorship</td> <td>church</td> <td>http://thenounproject.com/noun/church/#icon-No2541</td> </tr> <tr> <td>Playground</td> <td>playground</td> <td>http://thenounproject.com/noun/playground/#icon-No734</td> </tr> <tr> <td>PoliceStation</td> <td></td> <td></td> </tr> <tr> <td>RVPark</td> <td>rv</td> <td>http://thenounproject.com/noun/rv/#icon-No254</td> </tr> <tr> <td>StadiumOrArena</td> <td></td> <td></td> </tr> <tr> <td>SubwayStation</td> <td>subway</td> <td>http://thenounproject.com/noun/subway/#icon-No724</td> </tr> <tr> <td>TaxiStand</td> <td>taxi</td> <td>http://thenounproject.com/noun/taxi/#icon-No70</td> </tr> <tr> <td>TrainStation</td> <td>Train-station</td> <td>http://thenounproject.com/noun/train-station/#icon-No608</td> </tr> <tr> <td>Zoo</td> <td>elephant</td> <td>http://thenounproject.com/noun/elephant/#icon-No860</td> </tr> <tr> <td>Landform</td> <td>mountain</td> <td>http://thenounproject.com/noun/mountain/#icon-No1045</td> </tr> <tr> <td>BodyOfWater</td> <td>reservoir</td> <td>http://thenounproject.com/noun/reservoir/#icon-No1129</td> </tr> <tr> <td>Continent</td> <td></td> <td></td> </tr> <tr> <td>Mountain</td> <td>mountain</td> <td>http://thenounproject.com/noun/mountain/#icon-No1045</td> </tr> <tr> <td>Volcano</td> <td>volcano</td> <td>http://thenounproject.com/noun/volcano/#icon-No1708</td> </tr> <tr> <td>LandmarksOrHistoricalBuildings</td> <td>castle</td> <td>http://thenounproject.com/noun/castle/#icon-No2478</td> </tr> <tr> <td>Residence</td> <td>house</td> <td>http://thenounproject.com/noun/house/#icon-No1439</td> </tr> <tr> <td>TouristAttraction</td> <td>monument</td> <td>http://thenounproject.com/noun/monument/#icon-No2426</td> </tr> <tr> <td>Product</td> <td>present</td> <td>http://thenounproject.com/noun/present/#icon-No188</td> </tr> </tbody> </table>
    1. <!-- HTML -->
      <div class="text-container">
        <h1>Difference</h1>
      </div>
      <section></section>
      <section></section>
      
      /* CSS */
      section {
        width: 100vw;
        height: 100vh;
        background: #000;
        &:nth-child(odd) {
          background: #fff;
        }
      }
      .text-container {
        position: fixed;
        width: 100vw;
        height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
        color: #fff;
        mix-blend-mode: difference;
        h1 {
          font-size: 150px;
        }
      }
      
    1. Getting data from web archives using Memento

      Systems supporting the Memento protocol provide machine-readable information about web archive captures, even if other APIs are not available. In this notebook we'll look at the way the Memento protocol is supported across four web archive repositories – the UK Web Archive, the National Library of Australia, the National Library of New Zealand, and the Internet Archive. In particular we'll examine:

      • Timegates – request web page captures from (around) a particular date
      • Timemaps – request a list of web archive captures from a particular url
      • Mementos – use url modifiers to change the way an archived web page is presented
    1. Fetch and modify response properties which are immutable by creating a copy first.
      /**
       * @param {string} headerNameSrc Header to get the new value from
       * @param {string} headerNameDst Header to set based off of value in src
       */
      const headerNameSrc = "foo" //"Orig-Header"
      const headerNameDst = "Last-Modified"
      
      async function handleRequest(request) {
        /**
         * Response properties are immutable. To change them, construct a new
         * Response and pass modified status or statusText in the ResponseInit
         * object. Response headers can be modified through the headers `set` method.
         */
        const originalResponse = await fetch(request)
      
        // Change status and statusText, but preserve body and headers
        let response = new Response(originalResponse.body, {
          status: 500,
          statusText: "some message",
          headers: originalResponse.headers,
        })
      
        // Change response body by adding the foo prop
        const originalBody = await originalResponse.json()
        const body = JSON.stringify({ foo: "bar", ...originalBody })
        response = new Response(body, response)
      
        // Add a header using set method
        response.headers.set("foo", "bar")
      
        // Set destination header to the value of the source header
        const src = response.headers.get(headerNameSrc)
      
        if (src != null) {
          response.headers.set(headerNameDst, src)
          console.log(
            `Response header "${headerNameDst}" was set to "${response.headers.get(
              headerNameDst,
            )}"`,
          )
        }
        return response
      }
      
      addEventListener("fetch", event => {
        event.respondWith(handleRequest(event.request))
      })
      
    1. Globalize Selectors if Needed​

      [...]

      We refer to this pattern as "globalizing" selectors. A "globalized" selector is one that accepts the Redux root state as an argument, and knows how to find the relevant slice of state to perform the real logic. A "localized" selector is one that expects just a piece of the state as an argument, without knowing or caring where that is in the root state:

      // "Globalized" - accepts root state, knows to find data at `state.todos`
      const selectAllTodosCompletedGlobalized = state =>
        state.todos.every(todo => todo.completed)
      
      // "Localized" - only accepts `todos` as argument, doesn't know where that came from
      const selectAllTodosCompletedLocalized = todos =>
        todos.every(todo => todo.completed)
      

      "Localized" selectors can be turned into "globalized" selectors by wrapping them in a function that knows how to retrieve the right slice of state and pass it onwards.

      Redux Toolkit's createEntityAdapter API is an example of this pattern. If you call todosAdapter.getSelectors(), with no argument, it returns a set of "localized" selectors that expect the entity slice state as their argument. If you call todosAdapter.getSelectors(state => state.todos), it returns a set of "globalized" selectors that expect to be called with the Redux root state as their argument.

      There may also be other benefits to having "localized" versions of selectors as well. For example, say we have an advanced scenario of keeping multiple copies of createEntityAdapter data nested in the store, such as a chatRoomsAdapter that tracks rooms, and each room definition then has a chatMessagesAdapter state to store the messages. We can't directly look up the messages for each room - we first have to retrieve the room object, then select the messages out of that. This is easier if we have a set of "localized" selectors for the messages.

    2. Balance Selector Usage​

      [...]

      Similarly, don't make every single selector memoized!. Memoization is only needed if you are truly deriving results, and if the derived results would likely create new references every time. A selector function that does a direct lookup and return of a value should be a plain function, not memoized.

      Some examples of when and when not to memoize:

      // ❌ DO NOT memoize: will always return a consistent reference
      const selectTodos = state => state.todos
      const selectNestedValue = state => state.some.deeply.nested.field
      const selectTodoById = (state, todoId) => state.todos[todoId]
      
      // ❌ DO NOT memoize: deriving data, but will return a consistent result
      const selectItemsTotal = state => {
        return state.items.reduce((result, item) => {
          return result + item.total
        }, 0)
      }
      const selectAllCompleted = state => state.todos.every(todo => todo.completed)
      
      // ✅ SHOULD memoize: returns new references when called
      const selectTodoDescriptions = state => state.todos.map(todo => todo.text)
      
    3. Creating Unique Selector Instances​

      There are many cases where a selector function needs to be reused across multiple components. If the components will all be calling the selector with different arguments, it will break memoization - the selector never sees the same arguments multiple times in a row, and thus can never return a cached value.

      The standard approach here is to create a unique instance of a memoized selector in the component, and then use that with useSelector. That allows each component to consistently pass the same arguments to its own selector instance, and that selector can correctly memoize the results.

      For function components, this is normally done with useMemo or useCallback:

      import { makeSelectItemsByCategory } from './categoriesSlice'
      
      function CategoryList({ category }) {
        // Create a new memoized selector, for each component instance, on mount
        const selectItemsByCategory = useMemo(makeSelectItemsByCategory, [])
      
        const itemsByCategory = useSelector(state =>
          selectItemsByCategory(state, category)
        )
      }
      
    4. Using Selectors with React-Redux​

      • Calling Selectors with Parameters​

      It's common to want to pass additional arguments to a selector function. However, useSelector always calls the provided selector function with one argument - the Redux root state.

      The simplest solution is to pass an anonymous selector to useSelector, and then immediately call the real selector with both state and any additional arguments:

      import { selectTodoById } from './todosSlice'
      
      function TodoListitem({ todoId }) {
        // Captures `todoId` from scope, gets `state` as an arg, and forwards both
        // to the actual selector function to extract the result
        const todo = useSelector(state => selectTodoById(state, todoId))
      }
      
    5. Selector Factories​

      createSelector only has a default cache size of 1, and this is per each unique instance of a selector. This creates problems when a single selector function needs to get reused in multiple places with differing inputs.

      One option is to create a "selector factory" - a function that runs createSelector() and generates a new unique selector instance every time it's called:

      const makeSelectItemsByCategory = () => {
        const selectItemsByCategory = createSelector(
          [state => state.items, (state, category) => category],
          (items, category) => items.filter(item => item.category === category)
        )
        return selectItemsByCategory
      }
      

      This is particularly useful when multiple similar UI components need to derive different subsets of the data based on props.

    6. Passing Input Parameters​

      A Reselect-generated selector function can be called with as many arguments as you want: selectThings(a, b, c, d, e). However, what matters for re-running the output is not the number of arguments, or whether the arguments themselves have changed to be new references. Instead, it's about the "input selectors" that were defined, and whether their results have changed. Similarly, the arguments for the "output selector" are solely based on what the input selectors return.

      This means that if you want to pass additional parameters through to the output selector, you must define input selectors that extract those values from the original selector arguments:

      const selectItemsByCategory = createSelector(
        [
          // Usual first input - extract value from `state`
          state => state.items,
          // Take the second arg, `category`, and forward to the output selector
          (state, category) => category
        ],
        // Output selector gets (`items, category)` as args
        (items, category) => items.filter(item => item.category === category)
      )
      

      For consistency, you may want to consider passing additional parameters to a selector as a single object, such as selectThings(state, otherArgs), and then extracting values from the otherArgs object.

    7. Reselect Usage Patterns and Limitations​

      Nesting Selectors​

      It's possible to take selectors generated with createSelector, and use them as inputs for other selectors as well. In this example, the selectCompletedTodos selector is used as an input to selectCompletedTodoDescriptions:

      const selectTodos = state => state.todos

      const selectCompletedTodos = createSelector([selectTodos], todos => todos.filter(todo => todo.completed) )

      const selectCompletedTodoDescriptions = createSelector( [selectCompletedTodos], completedTodos => completedTodos.map(todo => todo.text) )

    8. Because of this, it's important that all of the "input selectors" you provide should accept the same types of parameters. Otherwise, the selectors will break.

      const selectItems = state => state.items;
      
      // expects a number as the second argument
      const selectItemId = (state, itemId) => itemId;
      
      // expects an object as the second argument
      const selectOtherField (state, someObject) => someObject.someField;
      
      const selectItemById = createSelector(
          [selectItems, selectItemId, selectOtherField],
          (items, itemId, someField) => items[itemId]
      );
      

      In this example, selectItemId expects that its second argument will be some simple value, while selectOtherField expects that the second argument is an object. If you call selectItemById(state, 42), selectOtherField will break because it's trying to access 42.someField.

    9. Also, you can pass multiple arguments into a selector. Reselect will call all of the input selectors with those exact inputs:
      const selectItems = state => state.items
      const selectItemId = (state, itemId) => itemId
      
      const selectItemById = createSelector(
        [selectItems, selectItemId],
        (items, itemId) => items[itemId]
      )
      
      const item = selectItemById(state, 42)
      
      /*
      Internally, Reselect does something like this:
      
      const firstArg = selectItems(state, 42);  
      const secondArg = selectItemId(state, 42);  
      
      const result = outputSelector(firstArg, secondArg);  
      return result;  
      */
      
    10. It's important to note that by default, createSelector only memoizes the most recent set of parameters. That means that if you call a selector repeatedly with different inputs, it will still return a result, but it will have to keep re-running the output selector to produce the result:
      const a = someSelector(state, 1) // first call, not memoized
      const b = someSelector(state, 1) // same inputs, memoized
      const c = someSelector(state, 2) // different inputs, not memoized
      const d = someSelector(state, 1) // different inputs from last time, not memoized
      
    11. In typical Reselect usage, you write your top-level "input selectors" as plain functions, and use createSelector to create memoized selectors that look up nested values:
      const state = {
        a: {
          first: 5
        },
        b: 10
      }
      
      const selectA = state => state.a
      const selectB = state => state.b
      
      const selectA1 = createSelector([selectA], a => a.first)
      
      const selectResult = createSelector([selectA1, selectB], (a1, b) => {
        console.log('Output selector running')
        return a1 + b
      })
      
      const result = selectResult(state)
      // Log: "Output selector running"
      console.log(result)
      // 15
      
      const secondResult = selectResult(state)
      // No log output
      console.log(secondResult)
      // 15
      

      Note that the second time we called selectResult, the "output selector" didn't execute. Because the results of selectA1 and selectB were the same as the first call, selectResult was able to return the memoized result from the first call.

    1. Jamiroquai (The Remix Project)

      • 0:00:00 Too Young To Die (Irregular Disco Workers Remix) by Jamiroquai
      • 0:06:53 Alright (Joshua Pathon Downtempo Mix) by Jamiroquai
      • 0:11:40 Deeper Underground (DJ Glen Remake) by Jamiroquai
      • 0:18:08 Space Cowboy (D.Morales RMX / Francesco Cofano 2014 Re-Touch) by Jamiroquai
      • 0:23:00 Cosmic Girl (Johnny Roostar Mix) by Jamiroquai
      • 0:28:38 You Give Me Something (Jon Parry Remix) by Jamiroquai
      • 0:33:58 Light Years (Micky Da Funk Bootleg Remix) by Jamiroquai
      • 0:40:01 Love Foolosophy (FM Groovin' Mix) by Jamiroquai
      • 0:44:45 Don't Give Hate A Chance (Mikeandtess Re-Edit) by Jamiroquai
      • 0:52:15 Bad Girls (Live at The Britts 2002) [No Legal House Mix] by Jamiroquai
      • 0:57:15 White Knuckle Ride (Penguin Remix) by Jamiroquai
      • 1:02:06 Supersonic (Manuel Figara Edit) by Jamiroquai
      • 1:08:20 Electric Mistress (Todd-If-Field-Mix) by Jamiroquai
      • 1:14:44 Lifeline (Shook Mix) by Jamiroquai
    1. Jamiroquai (The 2nd Remix Project):

      • 00:00 Stillness In Time (Timmy Regisford and Adam Rios Voc) by Jamiroquai
      • 06:28 Summer Girl (Walterino Remode) by Jamiroquai 12:44 Shake It On (Lavvy Levan Edit) by Jamiroquai
      • 18:10 Cloud 9 (Purple Disco Machine Remix) by Jamiroquai
      • 23:45 Too Young To Die (Rhemi Remix) by Jamiroquai
      • 29:12 Little L (Blaze Shelter Mix) by Jamiroquai
      • 35:20 Seven Days In Sunny June (Renato Bethlehem Mix) by Jamiroquai
      • 40:13 Corner of The Earth (T.R.A.R Vocal Mix) by Jamiroquai
      • 46:41 Runaway (Grant Nelson Remix) by Jamiroquai