559 Matching Annotations
  1. Last 7 days
    1. js function transclude(elementId) { var clone = document.getElementById(elementId).cloneNode(true), placeholder; clone.id = null; document.write('<br id="__placeholder__">'); placeholder = document.getElementById('__placeholder__'); placeholder.parentNode.insertBefore(clone, placeholder); placeholder.parentNode.removeChild(placeholder); return transclude; }

      ```html

      This is paragraph 1.

      This is paragraph 2.

      This is paragraph 3. It should contain paragraphs 1 and 2. <script>transclude("p1")("p2")</script>

      ```

    1. ```js / Adapted from: https://github.com/openannotation/annotator/blob/v1.2.x/src/plugin/document.coffee Annotator v1.2.10 https://github.com/openannotation/annotator Copyright 2015, the Annotator project contributors. Dual licensed under the MIT and GPLv3 licenses. https://github.com/openannotation/annotator/blob/master/LICENSE /

      /* * nb. The DocumentMetadata type is renamed to avoid a conflict with the * DocumentMetadata class below. * * @typedef {import('../../types/annotator').DocumentMetadata} Metadata /

      import { normalizeURI } from '../util/url';

      /* * @typedef Link * @prop {string} link.href * @prop {string} [link.rel] * @prop {string} [link.type] /

      /* * Extension of the Metadata type with non-optional fields for dc, eprints etc. * * @typedef HTMLDocumentMetadata * @prop {string} title * @prop {Link[]} link * @prop {Record<string, string[]>} dc * @prop {Record<string, string[]>} eprints * @prop {Record<string, string[]>} facebook * @prop {Record<string, string[]>} highwire * @prop {Record<string, string[]>} prism * @prop {Record<string, string[]>} twitter * @prop {string} [favicon] * @prop {string} [documentFingerprint] /

      / * HTMLMetadata reads metadata/links from the current HTML document. */ export class HTMLMetadata { / * @param {object} [options] * @param {Document} [options.document] */ constructor(options = {}) { this.document = options.document || document; }

      /* * Returns the primary URI for the document being annotated * * @return {string} / uri() { let uri = decodeURIComponent(this._getDocumentHref());

      // Use the `link[rel=canonical]` element's href as the URL if present.
      const links = this._getLinks();
      for (let link of links) {
        if (link.rel === 'canonical') {
          uri = link.href;
        }
      }
      
      return uri;
      

      }

      / * Return metadata for the current page. * * @return {HTMLDocumentMetadata} */ getDocumentMetadata() { / @type {HTMLDocumentMetadata} */ const metadata = { title: document.title, link: [],

        dc: this._getMetaTags('name', 'dc.'),
        eprints: this._getMetaTags('name', 'eprints.'),
        facebook: this._getMetaTags('property', 'og:'),
        highwire: this._getMetaTags('name', 'citation_'),
        prism: this._getMetaTags('name', 'prism.'),
        twitter: this._getMetaTags('name', 'twitter:'),
      };
      
      const favicon = this._getFavicon();
      if (favicon) {
        metadata.favicon = favicon;
      }
      
      metadata.title = this._getTitle(metadata);
      metadata.link = this._getLinks(metadata);
      
      const dcLink = metadata.link.find(link => link.href.startsWith('urn:x-dc'));
      if (dcLink) {
        metadata.documentFingerprint = dcLink.href;
      }
      
      return metadata;
      

      }

      / * Return an array of all the content values of <meta> tags on the page * where the value of the attribute begins with <prefix>. * * @param {string} attribute * @param {string} prefix - it is interpreted as a regex * @return {Record<string,string[]>} */ _getMetaTags(attribute, prefix) { / @type {Record<string,string[]>} */ const tags = {}; for (let meta of Array.from(this.document.querySelectorAll('meta'))) { const name = meta.getAttribute(attribute); const { content } = meta; if (name && content) { const match = name.match(RegExp(^${prefix}(.+)$, 'i')); if (match) { const key = match[1].toLowerCase(); if (tags[key]) { tags[key].push(content); } else { tags[key] = [content]; } } } } return tags; }

      /* @param {HTMLDocumentMetadata} metadata / _getTitle(metadata) { if (metadata.highwire.title) { return metadata.highwire.title[0]; } else if (metadata.eprints.title) { return metadata.eprints.title[0]; } else if (metadata.prism.title) { return metadata.prism.title[0]; } else if (metadata.facebook.title) { return metadata.facebook.title[0]; } else if (metadata.twitter.title) { return metadata.twitter.title[0]; } else if (metadata.dc.title) { return metadata.dc.title[0]; } else { return this.document.title; } }

      / * Get document URIs from <link> and <meta> elements on the page. * * @param {Pick<HTMLDocumentMetadata, 'highwire'|'dc'>} [metadata] - * Dublin Core and Highwire metadata parsed from <meta> tags. * @return {Link[]} */ _getLinks(metadata = { dc: {}, highwire: {} }) { / @type {Link[]} */ const links = [{ href: this._getDocumentHref() }];

      // Extract links from `<link>` tags with certain `rel` values.
      const linkElements = Array.from(this.document.querySelectorAll('link'));
      for (let link of linkElements) {
        if (
          !['alternate', 'canonical', 'bookmark', 'shortlink'].includes(link.rel)
        ) {
          continue;
        }
      
        if (link.rel === 'alternate') {
          // Ignore RSS feed links.
          if (link.type && link.type.match(/^application\/(rss|atom)\+xml/)) {
            continue;
          }
          // Ignore alternate languages.
          if (link.hreflang) {
            continue;
          }
        }
      
        try {
          const href = this._absoluteUrl(link.href);
          links.push({ href, rel: link.rel, type: link.type });
        } catch (e) {
          // Ignore URIs which cannot be parsed.
        }
      }
      
      // Look for links in scholar metadata
      for (let name of Object.keys(metadata.highwire)) {
        const values = metadata.highwire[name];
        if (name === 'pdf_url') {
          for (let url of values) {
            try {
              links.push({
                href: this._absoluteUrl(url),
                type: 'application/pdf',
              });
            } catch (e) {
              // Ignore URIs which cannot be parsed.
            }
          }
        }
      
        // Kind of a hack to express DOI identifiers as links but it's a
        // convenient place to look them up later, and somewhat sane since
        // they don't have a type.
        if (name === 'doi') {
          for (let doi of values) {
            if (doi.slice(0, 4) !== 'doi:') {
              doi = `doi:${doi}`;
            }
            links.push({ href: doi });
          }
        }
      }
      
      // Look for links in Dublin Core data
      for (let name of Object.keys(metadata.dc)) {
        const values = metadata.dc[name];
        if (name === 'identifier') {
          for (let id of values) {
            if (id.slice(0, 4) === 'doi:') {
              links.push({ href: id });
            }
          }
        }
      }
      
      // Look for a link to identify the resource in Dublin Core metadata
      const dcRelationValues = metadata.dc['relation.ispartof'];
      const dcIdentifierValues = metadata.dc.identifier;
      if (dcRelationValues && dcIdentifierValues) {
        const dcUrnRelationComponent =
          dcRelationValues[dcRelationValues.length - 1];
        const dcUrnIdentifierComponent =
          dcIdentifierValues[dcIdentifierValues.length - 1];
        const dcUrn =
          'urn:x-dc:' +
          encodeURIComponent(dcUrnRelationComponent) +
          '/' +
          encodeURIComponent(dcUrnIdentifierComponent);
        links.push({ href: dcUrn });
      }
      
      return links;
      

      }

      _getFavicon() { let favicon = null; for (let link of Array.from(this.document.querySelectorAll('link'))) { if (['shortcut icon', 'icon'].includes(link.rel)) { try { favicon = this._absoluteUrl(link.href); } catch (e) { // Ignore URIs which cannot be parsed. } } } return favicon; }

      /* * Convert a possibly relative URI to an absolute one. This will throw an * exception if the URL cannot be parsed. * * @param {string} url / _absoluteUrl(url) { return normalizeURI(url, this.document.baseURI); }

      // Get the true URI record when it's masked via a different protocol. // This happens when an href is set with a uri using the 'blob:' protocol // but the document can set a different uri through a <base> tag. _getDocumentHref() { const { href } = this.document.location; const allowedSchemes = ['http:', 'https:', 'file:'];

      // Use the current document location if it has a recognized scheme.
      const scheme = new URL(href).protocol;
      if (allowedSchemes.includes(scheme)) {
        return href;
      }
      
      // Otherwise, try using the location specified by the <base> element.
      if (
        this.document.baseURI &&
        allowedSchemes.includes(new URL(this.document.baseURI).protocol)
      ) {
        return this.document.baseURI;
      }
      
      // Fall back to returning the document URI, even though the scheme is not
      // in the allowed list.
      return href;
      

      } } ```

  2. Aug 2022
    1. If your site is using multiple widgets you can set up Twitter widgets in your pages once, which will make your site faster, and widgets such as embedded Tweets will be more reliable for authors when using content management systems.

      ```html

      <script>window.twttr = (function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0], t = window.twttr || {}; if (d.getElementById(id)) return t; js = d.createElement(s); js.id = id; js.src = "https://platform.twitter.com/widgets.js"; fjs.parentNode.insertBefore(js, fjs); t._e = []; t.ready = function(f) { t._e.push(f); }; return t; }(document, "script", "twitter-wjs"));</script>

      ```

  3. Jul 2022
    1. ```js const versions = [ { version: 1, data: 1}, { version: 3, data: 3}, { version: 17, data: 17}, { version: 95, data: 95} ];

      function getNextVersion(v) { return versions.slice(0).reduceRight((a , b, i , arr) => b.version <= v ? (arr.length = 0, b) : b) } ```

    1. Here's a complete function that removes accents, replaces special characters with hyphens, also removing additional hyphens:

      ```js const replaceSpecialChars = (str) => { return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '') // Remove accents .replace(/([^\w]+|\s+)/g, '-') // Replace space and other characters by hyphen .replace(/--+/g, '-') // Replaces multiple hyphens by one hyphen .replace(/(^-+|-+$)/g, ''); // Remove extra hyphens from beginning or end of the string }

      console.log(replaceSpecialChars('This is a sentence!!!')); ```

    1. ```js function replaceDiacritics(s) { var s;

      var diacritics =[ /[\300-\306]/g, /[\340-\346]/g, // A, a /[\310-\313]/g, /[\350-\353]/g, // E, e /[\314-\317]/g, /[\354-\357]/g, // I, i /[\322-\330]/g, /[\362-\370]/g, // O, o /[\331-\334]/g, /[\371-\374]/g, // U, u /[\321]/g, /[\361]/g, // N, n /[\307]/g, /[\347]/g, // C, c ];

      var chars = ['A','a','E','e','I','i','O','o','U','u','N','n','C','c'];

      for (var i = 0; i < diacritics.length; i++) { s = s.replace(diacritics[i],chars[i]); }

      document.write(s); } ```

    1. ```js const dbName = 'CustomPeople';

      const exists = await Dexie.exists(dbName);

      if (exists) { var db = new Dexie(dbName); const dynamicDB = await db.open(); const existingVersionNumber = dynamicDB.verno; const columns = await ExternalDBService.getColumns(); db.close(); db = new Dexie(dbName); db.version(existingVersionNumber).stores({ People: columns }); return db.open(); } else { db = new Dexie(dbName); db.version(1).stores({ People: [] }); db.open(); } ```

    1. ```js function formatBytes(bytes, decimals = 2) { if (bytes === 0) return '0 Bytes';

      const k = 1024; const dm = decimals < 0 ? 0 : decimals; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; } ```

    1. DOM 只是一个接口规范,可以用各种语言实现。所以严格地说,DOM 不是 JavaScript 语法的一部分,但是 DOM 操作是 JavaScript 最常见的任务,离开了 DOM,JavaScript 就无法控制网页。另一方面,JavaScript 也是最常用于 DOM 操作的语言

      DOM 的本质。Document Object Model. #share

    1. this可以用在构造函数之中,表示实例对象。除此之外,this还可以用在别的场合。但不管是什么场合,this都有一个共同点:它总是返回一个对象。

      this 指针总是返回一个对象。 #share

    1. JavaScript 语言使用构造函数(constructor)作为对象的模板。所谓”构造函数”,就是专门用来生成实例对象的函数

      构造函数作为对象的模板。 #share

    1. 所谓“包装对象”,指的是与数值、字符串、布尔值分别相对应的Number、String、Boolean三个原生对象。这三个原生对象可以把原始类型的值变成(包装成)对象。

      何为包装对象?#share

    1. 注意,forEach()方法无法中断执行,总是会将所有成员遍历完。如果希望符合某种条件时,就中断遍历,要使用for循环。

      forEach() 与 for 的区别 #share

  4. Jun 2022
    1. Creating GUID/UUID in Javascript using ES6 Crypto APIWe can use JavaScript ES6’s crypto API to generate GUID/UUID at the client side.crypto API comes up with a method called getRandomValues() which is used to generate a Universally Unique IDentifier as shown below

      js function CreateUUID() { return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) ) }

    1. However, if a TCO-capable engine can realize that the doA(b+1) call is in tail position meaning doB(b) is basically complete, then when calling doA(b+1), it doesn’t need to create a new stack frame, but can instead reuse the existing stack frame from doB(b). That’s faster and uses less memory.This sort of optimization becomes crucial when dealing with recursion, especially if the recursion could have resulted in thousands of stack frames. With TCO, the engine can perform all those calls in a single stack frame.
      Recursive function without TCO optimization

      ```js function factorial(n) { console.trace();

      if (n === 0) {
          return 1;
      }    // no proper tail call
      return n * factorial(n - 1);
      

      } factorial(2);VM31:4 console.trace factorial @ VM31:4VM31:4 console.trace factorial @ VM31:4 factorial @ VM31:10VM31:4 console.trace factorial @ VM31:4 factorial @ VM31:10 factorial @ VM31:10 ```

      Recursive function with TCO optimization

      js 'use strict'; function factorial(n, total = 1) { console.trace(); if (n === 0) { return total; } // proper tail call return factorial(n - 1, n * total); } factorial(2);Trace at factorial Trace at factorial Trace at factorial

    1. Implementation using readAsText()

      ```html <input type="file" id="file" /> <button id="get-time">Get Time</button>

      <script> document.getElementById('get-time').onclick = function () { let file = document.getElementById('file').files[0]; let fr = new FileReader(); fr.onload = function (e) { let startTime = getTime(e.target.result, false); let endTime = getTime(e.target.result, true); alert(`Log time range: ${startTime} ~ ${endTime}`); } fr.readAsText(file); } function getTime(text, reverse) { let timeReg = /\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2}/; for (let i = reverse ? text.length - 1 : 0; reverse ? i > -1 : i < text.length; reverse ? i-- : i++) { if (text[i].charCodeAt() === 10) { let snippet = text.substr(i + 1, 19); if (timeReg.exec(snippet)) { return snippet; } } } } </script>

      ```

      Implementation using readAsArrayBuffer()

      ```html <input type="file" id="file" /> <button id="get-time">Get Time</button>

      <script> document.getElementById('get-time').onclick = function () { let file = document.getElementById('file').files[0]; let fr = new FileReader(); let CHUNK_SIZE = 10 * 1024; let startTime, endTime; let reverse = false; fr.onload = function () { let buffer = new Uint8Array(fr.result); let timeReg = /\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2}/; for (let i = reverse ? buffer.length - 1 : 0; reverse ? i > -1 : i < buffer.length; reverse ? i-- : i++) { if (buffer[i] === 10) { let snippet = new TextDecoder('utf-8').decode(buffer.slice(i + 1, i + 20)); if (timeReg.exec(snippet)) { if (!reverse) { startTime = snippet; reverse = true; seek(); } else { endTime = snippet; alert(`Log time range: ${startTime} ~ ${endTime}`); } break; } } } } seek(); function seek() { let start = reverse ? file.size - CHUNK_SIZE : 0; let end = reverse ? file.size : CHUNK_SIZE; let slice = file.slice(start, end); fr.readAsArrayBuffer(slice); } } </script>

      ```

    1. ```html

      <input type="file" id="file-selector" multiple>

      <script> const fileSelector = document.getElementById('file-selector'); fileSelector.addEventListener('change', (event) => { const fileList = event.target.files; console.log(fileList); }); </script>

      ```

    1. The dataset read-only property of the HTMLElement interface provides read/write access to custom data attributes (data-*) on elements. It exposes a map of strings (DOMStringMap) with an entry for each data-* attribute.

      ```html

      <div id="user" data-id="1234567890" data-user="johndoe" data-date-of-birth>John Doe</div>

      ```

      ```js const el = document.querySelector('#user');

      // el.id === 'user' // el.dataset.id === '1234567890' // el.dataset.user === 'johndoe' // el.dataset.dateOfBirth === ''

      // set a data attribute el.dataset.dateOfBirth = '1960-10-03'; // Result on JS: el.dataset.dateOfBirth === '1960-10-03' // Result on HTML: <div id="user" data-id="1234567890" data-user="johndoe" data-date-of-birth="1960-10-03">John Doe</div>

      delete el.dataset.dateOfBirth; // Result on JS: el.dataset.dateOfBirth === undefined // Result on HTML: <div id="user" data-id="1234567890" data-user="johndoe">John Doe</div>

      if ('someDataAttr' in el.dataset === false) { el.dataset.someDataAttr = 'mydata'; // Result on JS: 'someDataAttr' in el.dataset === true // Result on HTML: <div id="user" data-id="1234567890" data-user="johndoe" data-some-data-attr="mydata">John Doe</div> } ```

    1. ```js if(window.location.href.endsWith('sitemap.xml')){

      const xmlStr = (
          `<?xml version="1.0" encoding="UTF-8"?>
              <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
                  <url><loc>https://erikmartinjordan.com</loc></url>
              </urlset>
          `
      );
      
      let blob = new Blob([xmlStr], {type: "text/xml"});
      let url = URL.createObjectURL(blob);
      window.open(url, "_self");
      URL.revokeObjectURL(url);
      

      } ```

    1. Using the download attribute to save a <canvas> as a PNGTo save a <canvas> element's contents as an image, you can create a link with a download attribute and the canvas data as a data: URL: Example painting app with save link

      ```html

      Paint by holding down the mouse button and moving it. Download my painting

      <canvas width="300" height="300"></canvas>

      ```

      css html { font-family: sans-serif; } canvas { background: #fff; border: 1px dashed; } a { display: inline-block; background: #69c; color: #fff; padding: 5px 10px; }

      ```js var canvas = document.querySelector('canvas'), c = canvas.getContext('2d'); c.fillStyle = 'hotpink';

      function draw(x, y) { if (isDrawing) { c.beginPath(); c.arc(x, y, 10, 0, Math.PI*2); c.closePath(); c.fill(); } }

      canvas.addEventListener('mousemove', event => draw(event.offsetX, event.offsetY) ); canvas.addEventListener('mousedown', () => isDrawing = true); canvas.addEventListener('mouseup', () => isDrawing = false);

      document.querySelector('a').addEventListener('click', event => event.target.href = canvas.toDataURL() ); ```

    1. Highlight part of an element This example uses the Range.setStart() and Range.setEnd() methods to add part of an address to a range. The selected range is then highlighted using Range.surroundContents(). The address contains nine nodes: five text nodes, and four <br> elements.

      ```html

      Wyatt Earp<br> 101 E. Main St.<br> Dodge City, KS<br> 67801<br> USA


      Nodes in the original address:

        ```

        ```js const address = document.getElementById('address'); const log = document.getElementById('log');

        // Log info address.childNodes.forEach(node => { const li = document.createElement('li'); li.textContent = ${node.nodeName}, ${node.nodeValue}; log.appendChild(li); });

        // Highlight the street and city const startOffset = 2; // Start at third node: 101 E. Main St. const endOffset = 5; // End at fifth node: Dodge City, KS const range = document.createRange(); range.setStart(address, startOffset); range.setEnd(address, endOffset);

        const mark = document.createElement('mark'); range.surroundContents(mark); ```

      1. ```html

        <html> <head> <br /> <script> function noMondays() { var tw = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null, false); var textNode = tw.nextNode(); while (textNode) { if (textNode.wholeText.match('Monday') || textNode.parentNode.getAttribute('id') == 'Monday') { textNode.parentNode.removeChild(textNode); } textNode = tw.nextNode(); } } function refresh() { window.location.reload( false ); // Reload our page. } </script>

        </head> <body>

        Monday, Joe bought a turkey.

        Tuesday, Bill bought a pound of nails.

        <div>Chuck called in sick Monday.</div>

        CALL supplier today!

        Wednesday's delivery was delayed.

        <button onclick="refresh()">Reload</button> <button onclick="noMondays()">Lose Mondays</button> </body>

        </html> ```

      1. DOMPurify - a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG
      1. If the component wrapped with memo re-renders, it means that one of its properties changes.
    2. May 2022
      1. ```html

        Choose your monster's colors:

        <div> <input type="color" id="head" name="head" value="#e66465"> <label for="head">Head</label> </div> <div> <input type="color" id="body" name="body" value="#f6b73c"> <label for="body">Body</label> </div>

        ```

        ```js colorPicker.addEventListener("input", updateFirst, false); colorPicker.addEventListener("change", watchColorPicker, false);

        function watchColorPicker(event) { document.querySelectorAll("p").forEach(function(p) { p.style.color = event.target.value; }); } ```

      1. JS is plenty fast and can be used for "large, complicated programs"[1] just fine. The problem with most JS written today is in programmer practices—the way that the community associated with NodeJS pushes one other to write code (which is, ironically, not even a good fit for the JavaScript language). It turns out that how you write code actually matters
      1. I feel like the point of the article isn't so much "how do I solve this specific issue" as "this is the general state of JS packaging", and the solution you present doesn't work in the general case of larger, less trivial dependencies

        Much of the (apparent) progress (i.e. activity—whether it constitutes busywork is another matter) in the world of "JS Prime" (that is, pre-Reformation, NodeJS/NPM-style development) is really about packaging problems.

      1. Anyway: I think the underlying problem is that it has been hidden that Node is NOT JavaScript. It uses (some) of the JavaScript syntax, but it doesn't use its standard library, and its not guaranteed that a Node package will also run on the browser.

        NodeJS development is often standards-incompatible with JS-the-language and other (actually standardized) vendor-neutral APIs.

      1. I've watched a bunch of very smart, highly-competent people bounce off JS; that's not a fault with them but with the ever-changing and overly-complicated ecosystem.

        It helps to be accurate (if we ever want to see these things fixed):

        They didn't "bounce off JS". They "bounced" after seeing NodeJS—and its community's common practices.

      1. If the media resource is embedded (for example in a iframe), Media Session API information must be set from the embedded context. See snippet below.

        ```html

        <iframe id="iframe"> <video>...</video> </iframe> <script> iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({ title: 'Never Gonna Give You Up', ... }); </script>

        ```

      1. React Helmet

        ```js import {Helmet} from "react-helmet";

        const Demo = props => ( <br /> <div className="application"> <Helmet> <script src="/path/to/resource.js" type="text/javascript" /> </Helmet> ... </div>

        ); ```

        DOM Method

        ```js componentDidMount () { const script = document.createElement("script"); script.src = "/path/to/resource.js"; script.async = true; document.body.appendChild(script); }

        export const appendScript = (scriptToAppend) => { const script = document.createElement("script"); script.src = scriptToAppend; script.async = true; document.body.appendChild(script); }

        class Demo extends React.Component { componentDidMount () { appendScript("/path/to/resource.js"); } } ```

        React Hooks

        js useEffect(() => { const script = document.createElement('script'); script.src = "/path/to/resource.js"; script.async = true; document.body.appendChild(script); return () => { document.body.removeChild(script); } }, []);

        ```js import { useEffect } from 'react'; const importScript = resourceUrl=> { useEffect(() => { const script = document.createElement('script'); script.src = resourceUrl; script.async = true; document.body.appendChild(script); return () => { document.body.removeChild(script); } }, [resourceUrl]); };

        const Demo = props => { importScript("/path/to/resource.js"); } ```

      1. The new lines you mention really are present in the text content of the element. HTML tags are not being replaced by new lines, they just get omitted entirely. If you look at the textContent property of the <p> element you selected in the browser console, and you'll see the same new lines. Also if you select the text and run window.getSelection().getRangeAt(0).toString() in the browser console you'll see the same new lines. In summary, this is working as it is currently expected to. What I think may have been surprising here is that the captured text is not the same as what would be copied to the clipboard. When copying to the clipboard, new lines in the source get replaced with spaces, and <br> tags get converted to new lines. Browser specifications distinguish the original text content of HTML "in the source" as returned by element.textContent from the text content "as rendered" returned by element.innerText. Hypothesis has always captured quotes from and searched for quotes in the "source" text content rather than the "rendered" text. This behavior causes issues with line breaks as well. It might make sense for us to look at capturing the rendered text (as copied to the clipboard) rather than the source text in future. We'd need to be careful to handle all the places where this distinction comes up, and also make sure that all existing annotations anchor properly. Also we should talk to other parties interested in the Web Annotations specifications to discuss how this impacts interoperability.
        What I think may have been surprising here is that the captured text is not the same as what would be copied to the clipboard. When <mark>copying to the clipboard, <mark style="background-color: #8000314f">new lines in the source</mark> get <mark style="background-color:#00800030">replaced with spaces</mark>, and <br> tags get converted to new lines</mark>. </br> <mark>Browser specifications distinguish <mark style="background-color: #00800036">the original text content of HTML "in the source"</mark> as returned by <mark style="background-color: #00800036"/>element.textContent</mark> from <mark style="background-color: #ffa500a1">the text content "as rendered" returned by element.innerText.</mark></mark> Hypothesis has always captured quotes from and searched for quotes in the "source" text content rather than the "rendered" text.
      1. Differences from innerHTML Element.innerHTML returns HTML, as its name indicates. Sometimes people use innerHTML to retrieve or write text inside an element, but textContent has better performance because its value is not parsed as HTML. Moreover, using textContent can prevent XSS attacks.
      2. Differences from innerText Don't get confused by the differences between Node.textContent and HTMLElement.innerText. Although the names seem similar, there are important differences: textContent gets the content of all elements, including <script> and <style> elements. In contrast, innerText only shows "human-readable" elements. textContent returns every element in the node. In contrast, innerText is aware of styling and won't return the text of "hidden" elements. Moreover, since innerText takes CSS styles into account, reading the value of innerText triggers a reflow to ensure up-to-date computed styles. (Reflows can be computationally expensive, and thus should be avoided when possible.) Both textContent and innerText remove child nodes when altered, but altering innerText in Internet Explorer (version 11 and below) also permanently destroys all descendant text nodes. It is impossible to insert the nodes again into any other element or into the same element after doing so.
      1. With some extra work, you can download the entire text version of Moby-Dick from Project Gutenberg using Axios.

        ```js const axios = require('axios'); const epub = require('epub-gen');

        axios.get('http://www.gutenberg.org/files/2701/2701-0.txt'). then(res => res.data). then(text => { text = text.slice(text.indexOf('EXTRACTS.')); text = text.slice(text.indexOf('CHAPTER 1.'));

        const lines = text.split('\r\n');
        const content = [];
        for (let i = 0; i < lines.length; ++i) {
          const line = lines[i];
          if (line.startsWith('CHAPTER ')) {
            if (content.length) {
              content[content.length - 1].data = content[content.length - 1].data.join('\n');
            }
            content.push({
              title: line,
              data: ['<h2>' + line + '</h2>']
            });
          } else if (line.trim() === '') {
            if (content[content.length - 1].data.length > 1) {
              content[content.length - 1].data.push('</p>');
            }
            content[content.length - 1].data.push('<p>');
          } else {
            content[content.length - 1].data.push(line);
          }
        }
        
        const options = {
          title: 'Moby-Dick',
          author: 'Herman Melville',
          output: './moby-dick.epub',
          content
        };
        
        return new epub(options).promise;
        

        }). then(() => console.log('Done')); ```

      1. Say hello to the sassy new Media Queries!

        js const html = document.getElementsByTagName('html')[0]; const toggleTheme = (theme) => { html.dataset.theme = theme; } html <script> // If `prefers-color-scheme` is not supported, fall back to light mode. // In this case, light.css will be downloaded with `highest` priority. if (window.matchMedia('(prefers-color-scheme: dark)').media === 'not all') { document.documentElement.style.display = 'none'; document.head.insertAdjacentHTML( 'beforeend', '<link rel="stylesheet" href="/light.css" onload="document.documentElement.style.display = \'\'">' ); } </script> <!-- Conditionally either load the light or the dark stylesheet. The matching file will be downloaded with `highest`, the non-matching file with `lowest` priority. If the browser doesn't support `prefers-color-scheme`, the media query is unknown and the files are downloaded with `lowest` priority (but above I already force `highest` priority for my default light experience). --> <link rel="stylesheet" href="/dark.css" media="(prefers-color-scheme: dark)"> <link rel="stylesheet" href="/light.css" media="(prefers-color-scheme: light)"> <!-- The main stylesheet --> <link rel="stylesheet" href="/style.css">

        html <img src="./sun.svg" data-light-src="./sun.svg" data-dark-src="./moon.svg" alt="light theme" id="theme-selector" onclick="switchTheme(this)">

      1. Also referred to as the backend development, Express.js is popularly used to design a web application’s behind-the-scenes architecture. The backend of an application is not visible to the end-users, but it exists. It is an inevitable part of the web application.
    3. blog.cloudflare.com