<commit_before>import { extend } from 'flarum/extend';
import CommentPost from 'flarum/components/CommentPost';
import PostPreview from 'flarum/components/PostPreview';
import LoadingIndicator from 'flarum/components/LoadingIndicator';

export default function addPostMentionPreviews() {
  extend(CommentPost.prototype, 'config', function() {
    const contentHtml = this.props.post.contentHtml();

    if (contentHtml === this.oldPostContentHtml) return;

    this.oldPostContentHtml = contentHtml;

    const discussion = this.props.post.discussion();

    this.$('.UserMention, .PostMention').each(function() {
      m.route.call(this, this, false, {}, {attrs: {href: this.getAttribute('href')}});
    });

    this.$('.PostMention').each(function() {
      const $this = $(this);
      const number = $this.data('number');
      let timeout;

      // Wrap the mention link in a wrapper element so that we can insert a
      // preview popup as its sibling and relatively position it.
      const $preview = $('
');
      const $wrapper = $('');
      $this.wrap($wrapper).after($preview);

      const getPostElement = () => {
        return $(`.PostStream-item[data-number="${number}"]`);
      };

      const showPreview = () => {
        // When the user hovers their mouse over the mention, look for the
        // post that it's referring to in the stream, and determine if it's
        // in the viewport. If it is, we will "pulsate" it.
        const $post = getPostElement();
        let visible = false;
        if ($post.length) {
          const top = $post.offset().top;
          const scrollTop = window.pageYOffset;
          if (top > scrollTop && top + $post.height() < scrollTop + $(window).height()) {
            $post.addClass('pulsate');
            visible = true;
          }
        }

        // Otherwise, we will show a popup preview of the post. If the post
        // hasn't yet been loaded, we will need to do that.
        if (!visible) {
          // Position the preview so that it appears above the mention.
          // (The offsetParent should be .Post-body.)
          const positionPreview = () => {
            $preview.show().css('top', $this.offset().top - $this.offsetParent().offset().top - $preview.outerHeight(true));
          };

          const showPost = post => {
            m.render($preview[0],
{PostPreview.component({post})}
);
            positionPreview();
          };

          const post = discussion.posts().filter(p => p && p.number() === number)[0];
          if (post) {
            showPost(post);
          } else {
            m.render($preview[0], LoadingIndicator.component());
            app.store.find('posts', {
              filter: {discussion: discussion.id(), number}
            }).then(posts => showPost(posts[0]));
            positionPreview();
          }

          setTimeout(() => $preview.off('transitionend').addClass('in'));
        }
      };

      const hidePreview = () => {
        getPostElement().removeClass('pulsate');
        if ($preview.hasClass('in')) {
          $preview.removeClass('in').one('transitionend', () => $preview.hide());
        }
      };

      $this.parent().hover(
        () => {
          clearTimeout(timeout);
          timeout = setTimeout(showPreview, 500);
        },
        () => {
          clearTimeout(timeout);
          getPostElement().removeClass('pulsate');
          timeout = setTimeout(hidePreview, 250);
        }
      )
        .on('touchstart', e => e.preventDefault())
        .on('touchend', e => {
          showPreview();
          e.stopPropagation();
        });
      $(document).on('touchend', hidePreview);
    });
  });
}
<commit_msg>Ensure post mention previews are clickable on mobile<commit_after>import { extend } from 'flarum/extend';
import CommentPost from 'flarum/components/CommentPost';
import PostPreview from 'flarum/components/PostPreview';
import LoadingIndicator from 'flarum/components/LoadingIndicator';

export default function addPostMentionPreviews() {
  extend(CommentPost.prototype, 'config', function() {
    const contentHtml = this.props.post.contentHtml();

    if (contentHtml === this.oldPostContentHtml) return;

    this.oldPostContentHtml = contentHtml;

    const discussion = this.props.post.discussion();

    this.$('.UserMention, .PostMention').each(function() {
      m.route.call(this, this, false, {}, {attrs: {href: this.getAttribute('href')}});
    });

    this.$('.PostMention').each(function() {
      const $this = $(this);
      const number = $this.data('number');
      let timeout;

      // Wrap the mention link in a wrapper element so that we can insert a
      // preview popup as its sibling and relatively position it.
      const $preview = $('
');
      const $wrapper = $('');
      $this.wrap($wrapper).after($preview);

      const getPostElement = () => {
        return $(`.PostStream-item[data-number="${number}"]`);
      };

      const showPreview = () => {
        // When the user hovers their mouse over the mention, look for the
        // post that it's referring to in the stream, and determine if it's
        // in the viewport. If it is, we will "pulsate" it.
        const $post = getPostElement();
        let visible = false;
        if ($post.length) {
          const top = $post.offset().top;
          const scrollTop = window.pageYOffset;
          if (top > scrollTop && top + $post.height() < scrollTop + $(window).height()) {
            $post.addClass('pulsate');
            visible = true;
          }
        }

        // Otherwise, we will show a popup preview of the post. If the post
        // hasn't yet been loaded, we will need to do that.
        if (!visible) {
          // Position the preview so that it appears above the mention.
          // (The offsetParent should be .Post-body.)
          const positionPreview = () => {
            $preview.show().css('top', $this.offset().top - $this.offsetParent().offset().top - $preview.outerHeight(true));
          };

          const showPost = post => {
            m.render($preview[0],
{PostPreview.component({post})}
);
            positionPreview();
          };

          const post = discussion.posts().filter(p => p && p.number() === number)[0];
          if (post) {
            showPost(post);
          } else {
            m.render($preview[0], LoadingIndicator.component());
            app.store.find('posts', {
              filter: {discussion: discussion.id(), number}
            }).then(posts => showPost(posts[0]));
            positionPreview();
          }

          setTimeout(() => $preview.off('transitionend').addClass('in'));
        }
      };

      const hidePreview = () => {
        getPostElement().removeClass('pulsate');
        if ($preview.hasClass('in')) {
          $preview.removeClass('in').one('transitionend', () => $preview.hide());
        }
      };

      $this.on('touchstart', e => e.preventDefault());

      $this.parent().hover(
        () => {
          clearTimeout(timeout);
          timeout = setTimeout(showPreview, 500);
        },
        () => {
          clearTimeout(timeout);
          getPostElement().removeClass('pulsate');
          timeout = setTimeout(hidePreview, 250);
        }
      )
        .on('touchend', e => {
          showPreview();
          e.stopPropagation();
        });

      $(document).on('touchend', hidePreview);
    });
  });
}
<|endoftext|>


<commit_before>/*
 *  @module Clickable
 *  @namespace component
 *  @desc Used to make a game component perfom an action when she's clicked
 *  @copyright (C) SpilGames
 *  @author Jeroen Reurings
 *  @license BSD 3-Clause License (see LICENSE file in project root)
 */
glue.module.create(
    'glue/component/clickable',
    [
        'glue',
        'glue/basecomponent'
    ],
    function (Glue, BaseComponent) {
        'use strict';

        return function (object) {
            var baseComponent = BaseComponent('clickable', object),
                isClicked = function (e) {
                    return object.getBoundingBox().hasPosition(e.position);
                },
                pointerDownHandler = function (e) {
                    if (isClicked(e) && object.onClickDown) {
                        object.onClickDown(e);
                    }
                },
                pointerUpHandler = function (e) {
                    if (isClicked(e) && object.onClickUp) {
                        object.onClickUp(e);
                    }
                };

            baseComponent.set({
                setup: function (settings) {

                },
                update: function (deltaT) {

                },
                pointerDown: function (e) {
                    pointerDownHandler(e);
                },
                pointerUp: function (e) {
                    pointerUpHandler(e);
                },
                register: function () {
                    baseComponent.register('pointerDown');
                    baseComponent.register('pointerUp');
                },
                unregister: function () {
                    baseComponent.unregister('pointerDown');
                    baseComponent.unregister('pointerUp');
                }
            });

            return object;
        };
    }
);
<commit_msg>Make it backwards compatible<commit_after>/*
 *  @module Clickable
 *  @namespace component
 *  @desc Used to make a game component perfom an action when she's clicked
 *  @copyright (C) SpilGames
 *  @author Jeroen Reurings
 *  @license BSD 3-Clause License (see LICENSE file in project root)
 */
glue.module.create(
    'glue/component/clickable',
    [
        'glue',
        'glue/basecomponent'
    ],
    function (Glue, BaseComponent) {
        'use strict';

        return function (object) {
            var baseComponent = BaseComponent('clickable', object),
                isClicked = function (e) {
                    return object.getBoundingBox().hasPosition(e.position);
                },
                pointerDownHandler = function (e) {
                    if (isClicked(e)) {
                        if (object.onClickDown) {
                            object.onClickDown(e);
                        }
                        if (object.onClick) {
                            object.onClick(e);
                        }
                    }
                },
                pointerUpHandler = function (e) {
                    if (isClicked(e) && object.onClickUp) {
                        object.onClickUp(e);
                    }
                };

            baseComponent.set({
                setup: function (settings) {

                },
                update: function (deltaT) {

                },
                pointerDown: function (e) {
                    pointerDownHandler(e);
                },
                pointerUp: function (e) {
                    pointerUpHandler(e);
                },
                register: function () {
                    baseComponent.register('pointerDown');
                    baseComponent.register('pointerUp');
                },
                unregister: function () {
                    baseComponent.unregister('pointerDown');
                    baseComponent.unregister('pointerUp');
                }
            });

            return object;
        };
    }
);
<|endoftext|>


<commit_before>'use strict'

import * as input from '../input/input.js'

import styles from './timer.css'

// function to create a 'timer' instance
export const Timer = ({
startTime = new Date().getTime(),
pomodoro = input.userInputs.pomodoroLength,
shortBreak = input.userInputs.shortBreakLength,
longBreak = input.userInputs.longBreakLength,
pomodoroCount = 0,
currentActivity = 'pomodoro',
endTime = startTime + (pomodoro * 60000)
} = {}) => ({
  startTime,
  pomodoro,
  shortBreak,
  longBreak,
  pomodoroCount,
  currentActivity,
  endTime,
  timerID: 0
})

export function beginTimer (timer = Timer()) {
  timer.timerID = setTimeout(checkTimer, 1000, timer)
}

export function checkTimer (timerToCheck) {
  let currentTime = new Date().getTime()
  if (currentTime >= timerToCheck.endTime) {
    finishTimer(timerToCheck)
    console.log('finish')
  } else {
    console.log('tick')
    timerToCheck.timerID = setTimeout(checkTimer, 1000, timerToCheck)
  }
}

export function finishTimer (previousTimer) {
  if (previousTimer.currentActivity === 'pomodoro') {
    let newEndtime = new Date().getTime() + (previousTimer.shortBreak * 60000)
    beginTimer(Timer({
      pomodoroCount: previousTimer.pomodoroCount + 1,
      currentActivity: 'shortBreak',
      endTime: newEndtime
    }))
  }
}
<commit_msg>finishTimer now starts a Short Break at the end of each Pomodoro<commit_after>'use strict'

import * as input from '../input/input.js'

import styles from './timer.css'

// function to create a 'timer' instance
export const Timer = ({
startTime = new Date().getTime(),
pomodoro = input.userInputs.pomodoroLength,
shortBreak = input.userInputs.shortBreakLength,
longBreak = input.userInputs.longBreakLength,
pomodoroCount = 0,
currentActivity = 'pomodoro',
endTime = startTime + (pomodoro * 60000)
} = {}) => ({
  startTime,
  pomodoro,
  shortBreak,
  longBreak,
  pomodoroCount,
  currentActivity,
  endTime,
  timerID: 0
})

export function beginTimer (timer = Timer()) {
  timer.timerID = setTimeout(checkTimer, 1000, timer)
}

export function checkTimer (timerToCheck) {
  let currentTime = new Date().getTime()
  if (currentTime >= timerToCheck.endTime) {
    finishTimer(timerToCheck)
  } else {
    console.log('tick')
    timerToCheck.timerID = setTimeout(checkTimer, 1000, timerToCheck)
  }
}

export function finishTimer (previousTimer) {
  if (previousTimer.currentActivity === 'pomodoro') {
    console.log('finished pomodoro #' + previousTimer.pomodoroCount)
    let newEndtime = new Date().getTime() + (previousTimer.shortBreak * 60000)
    beginTimer(Timer({
      pomodoroCount: previousTimer.pomodoroCount + 1,
      currentActivity: 'shortBreak',
      endTime: newEndtime
    }))
  } else {
    console.log('finished break')
    let newEndtime = new Date().getTime() + (previousTimer.pomodoro * 60000)
    beginTimer(Timer({
      pomodoroCount: previousTimer.pomodoroCount,
      currentActivity: 'pomodoro',
      endTime: newEndtime
    }))
  }
}
<|endoftext|>


<commit_before>// ::- A specification for serializing a ProseMirror document as
// Markdown/CommonMark text.
export class MarkdownSerializer {
  // :: (Object<(state: MarkdownSerializerState, node: Node, parent: Node, index: number)>, Object)

  // Construct a serializer with the given configuration. The `nodes`
  // object should map node names in a given schema to function that
  // take a serializer state and such a node, and serialize the node.
  //
  // The `marks` object should hold objects with `open` and `close`
  // properties, which hold the strings that should appear before and
  // after a piece of text marked that way, either directly or as a
  // function that takes a serializer state and a mark, and returns a
  // string.
  //
  // Mark information objects can also have a `mixable` property
  // which, when `true`, indicates that the order in which the mark's
  // opening and closing syntax appears relative to other mixable
  // marks can be varied. (For example, you can say `**a *b***` and
  // `*a **b***`, but not `` `a *b*` ``.)
  //
  // The `expelEnclosingWhitespace` mark property causes the
  // serializer to move enclosing whitespace from inside the marks to
  // outside the marks. This is necessary for emphasis marks as
  // CommonMark does not permit enclosing whitespace inside emphasis
  // marks, see: http://spec.commonmark.org/0.26/#example-330
  constructor(nodes, marks) {
    // :: Object<(MarkdownSerializerState, Node)> The node serializer
    // functions for this serializer.
    this.nodes = nodes
    // :: Object The mark serializer info.
    this.marks = marks
  }

  // :: (Node, ?Object) → string
  // Serialize the content of the given node to
  // [CommonMark](http://commonmark.org/).
  serialize(content, options) {
    let state = new MarkdownSerializerState(this.nodes, this.marks, options)
    state.renderContent(content)
    return state.out
  }
}

// :: MarkdownSerializer
// A serializer for the [basic schema](#schema).
export const defaultMarkdownSerializer = new MarkdownSerializer({
  blockquote(state, node) {
    state.wrapBlock("> ", null, node, () => state.renderContent(node))
  },
  code_block(state, node) {
    state.write("```" + node.attrs.params + "\n")
    state.text(node.textContent, false)
    state.ensureNewLine()
    state.write("```")
    state.closeBlock(node)
  },
  heading(state, node) {
    state.write(state.repeat("#", node.attrs.level) + " ")
    state.renderInline(node)
    state.closeBlock(node)
  },
  horizontal_rule(state, node) {
    state.write(node.attrs.markup || "---")
    state.closeBlock(node)
  },
  bullet_list(state, node) {
    state.renderList(node, "  ", () => (node.attrs.bullet || "*") + " ")
  },
  ordered_list(state, node) {
    let start = node.attrs.order || 1
    let maxW = String(start + node.childCount - 1).length
    let space = state.repeat(" ", maxW + 2)
    state.renderList(node, space, i => {
      let nStr = String(start + i)
      return state.repeat(" ", maxW - nStr.length) + nStr + ". "
    })
  },
  list_item(state, node) {
    state.renderContent(node)
  },
  paragraph(state, node) {
    state.renderInline(node)
    state.closeBlock(node)
  },

  image(state, node) {
    state.write("![" + state.esc(node.attrs.alt || "") + "](" + state.esc(node.attrs.src) +
                (node.attrs.title ? " " + state.quote(node.attrs.title) : "") + ")")
  },
  hard_break(state, node, parent, index) {
    for (let i = index + 1; i < parent.childCount; i++)
      if (parent.child(i).type != node.type) {
        state.write("\\\n")
        return
      }
  },
  text(state, node) {
    state.text(node.text)
  }
}, {
  em: {open: "*", close: "*", mixable: true, expelEnclosingWhitespace: true},
  strong: {open: "**", close: "**", mixable: true, expelEnclosingWhitespace: true},
  link: {
    open: "[",
    close(state, mark) {
      return "](" + state.esc(mark.attrs.href) + (mark.attrs.title ? " " + state.quote(mark.attrs.title) : "") + ")"
    }
  },
  code: {open: "`", close: "`"}
})

// ::- This is an object used to track state and expose
// methods related to markdown serialization. Instances are passed to
// node and mark serialization methods (see `toMarkdown`).
export class MarkdownSerializerState {
  constructor(nodes, marks, options) {
    this.nodes = nodes
    this.marks = marks
    this.delim = this.out = ""
    this.closed = false
    this.inTightList = false
    // :: Object
    // The options passed to the serializer.
    //   tightLists:: ?bool
    //   Whether to render lists in a tight style. This can be overridden
    //   on a node level by specifying a tight attribute on the node.
    //   Defaults to false.
    this.options = options || {}
    if (typeof this.options.tightLists == "undefined")
      this.options.tightLists = false
  }

  flushClose(size) {
    if (this.closed) {
      if (!this.atBlank()) this.out += "\n"
      if (size == null) size = 2
      if (size > 1) {
        let delimMin = this.delim
        let trim = /\s+$/.exec(delimMin)
        if (trim) delimMin = delimMin.slice(0, delimMin.length - trim[0].length)
        for (let i = 1; i < size; i++)
          this.out += delimMin + "\n"
      }
      this.closed = false
    }
  }

  // :: (string, ?string, Node, ())
  // Render a block, prefixing each line with `delim`, and the first
  // line in `firstDelim`. `node` should be the node that is closed at
  // the end of the block, and `f` is a function that renders the
  // content of the block.
  wrapBlock(delim, firstDelim, node, f) {
    let old = this.delim
    this.write(firstDelim || delim)
    this.delim += delim
    f()
    this.delim = old
    this.closeBlock(node)
  }

  atBlank() {
    return /(^|\n)$/.test(this.out)
  }

  // :: ()
  // Ensure the current content ends with a newline.
  ensureNewLine() {
    if (!this.atBlank()) this.out += "\n"
  }

  // :: (?string)
  // Prepare the state for writing output (closing closed paragraphs,
  // adding delimiters, and so on), and then optionally add content
  // (unescaped) to the output.
  write(content) {
    this.flushClose()
    if (this.delim && this.atBlank())
      this.out += this.delim
    if (content) this.out += content
  }

  // :: (Node)
  // Close the block for the given node.
  closeBlock(node) {
    this.closed = node
  }

  // :: (string, ?bool)
  // Add the given text to the document. When escape is not `false`,
  // it will be escaped.
  text(text, escape) {
    let lines = text.split("\n")
    for (let i = 0; i < lines.length; i++) {
      var startOfLine = this.atBlank() || this.closed
      this.write()
      this.out += escape !== false ? this.esc(lines[i], startOfLine) : lines[i]
      if (i != lines.length - 1) this.out += "\n"
    }
  }

  // :: (Node)
  // Render the given node as a block.
  render(node, parent, index) {
    if (typeof parent == "number") throw new Error("!")
    this.nodes[node.type.name](this, node, parent, index)
  }

  // :: (Node)
  // Render the contents of `parent` as block nodes.
  renderContent(parent) {
    parent.forEach((node, _, i) => this.render(node, parent, i))
  }

  // :: (Node)
  // Render the contents of `parent` as inline content.
  renderInline(parent) {
    let active = [], trailing = ""
    let progress = (node, _, index) => {
      let marks = node ? node.marks : []

      let leading = trailing
      trailing = ""
      // If whitespace has to be expelled from the node, adjust
      // leading and trailing accordingly.
      if (node && node.isText && marks.some(mark => {
        let info = this.marks[mark.type.name]
        return info && info.expelEnclosingWhitespace
      })) {
        let [_, lead, inner, trail] = /^(\s*)(.*?)(\s*)$/m.exec(node.text)
        leading += lead
        trailing = trail
        if (lead || trail) {
          node = inner ? node.withText(inner) : null
          if (!node) marks = active
        }
      }

      let code = marks.length && marks[marks.length - 1].type.isCode && marks[marks.length - 1]
      let len = marks.length - (code ? 1 : 0)

      // Try to reorder 'mixable' marks, such as em and strong, which
      // in Markdown may be opened and closed in different order, so
      // that order of the marks for the token matches the order in
      // active.
      outer: for (let i = 0; i < len; i++) {
        let mark = marks[i]
        if (!this.marks[mark.type.name].mixable) break
        for (let j = 0; j < active.length; j++) {
          let other = active[j]
          if (!this.marks[other.type.name].mixable) break
          if (mark.eq(other)) {
            if (i > j)
              marks = marks.slice(0, j).concat(mark).concat(marks.slice(j, i)).concat(marks.slice(i + 1, len))
            else if (j > i)
              marks = marks.slice(0, i).concat(marks.slice(i + 1, j)).concat(mark).concat(marks.slice(j, len))
            continue outer
          }
        }
      }

      // Find the prefix of the mark set that didn't change
      let keep = 0
      while (keep < Math.min(active.length, len) && marks[keep].eq(active[keep])) ++keep

      // Close the marks that need to be closed
      while (keep < active.length)
        this.text(this.markString(active.pop(), false), false)

      // Output any previously expelled trailing whitespace outside the marks
      if (leading) this.text(leading)

      // Open the marks that need to be opened
      if (node) {
        while (active.length < len) {
          let add = marks[active.length]
          active.push(add)
          this.text(this.markString(add, true), false)
        }

        // Render the node. Special case code marks, since their content
        // may not be escaped.
        if (code && node.isText)
          this.text(this.markString(code, false) + node.text + this.markString(code, true), false)
        else
          this.render(node, parent, index)
      }
    }
    parent.forEach(progress)
    progress(null)
  }

  // :: (Node, string, (number) → string)
  // Render a node's content as a list. `delim` should be the extra
  // indentation added to all lines except the first in an item,
  // `firstDelim` is a function going from an item index to a
  // delimiter for the first line of the item.
  renderList(node, delim, firstDelim) {
    if (this.closed && this.closed.type == node.type)
      this.flushClose(3)
    else if (this.inTightList)
      this.flushClose(1)

    let isTight = typeof node.attrs.tight != "undefined" ? node.attrs.tight : this.options.tightLists
    let prevTight = this.inTightList
    this.inTightList = isTight
    node.forEach((child, _, i) => {
      if (i && isTight) this.flushClose(1)
      this.wrapBlock(delim, firstDelim(i), node, () => this.render(child, node, i))
    })
    this.inTightList = prevTight
  }

  // :: (string, ?bool) → string
  // Escape the given string so that it can safely appear in Markdown
  // content. If `startOfLine` is true, also escape characters that
  // has special meaning only at the start of the line.
  esc(str, startOfLine) {
    str = str.replace(/[`*\\~\[\]]/g, "\\$&")
    if (startOfLine) str = str.replace(/^[:#-*+]/, "\\$&").replace(/^(\d+)\./, "$1\\.")
    return str
  }

  quote(str) {
    var wrap = str.indexOf('"') == -1 ? '""' : str.indexOf("'") == -1 ? "''" : "()"
    return wrap[0] + str + wrap[1]
  }

  // :: (string, number) → string
  // Repeat the given string `n` times.
  repeat(str, n) {
    let out = ""
    for (let i = 0; i < n; i++) out += str
    return out
  }

  // : (Mark, bool) → string
  // Get the markdown string for a given opening or closing mark.
  markString(mark, open) {
    let info = this.marks[mark.type.name]
    let value = open ? info.open : info.close
    return typeof value == "string" ? value : value(this, mark)
  }

  // :: (string) → { leading: ?string, trailing: ?string }
  // Get leading and trailing whitespace from a string. Values of
  // leading or trailing property of the return object will be undefined
  // if there is no match.
  getEnclosingWhitespace(text) {
    return {
      leading: (text.match(/^(\s+)/) || [])[0],
      trailing: (text.match(/(\s+)$/) || [])[0]
    }
  }

}
<commit_msg>Fix mistake in escaping regexp<commit_after>// ::- A specification for serializing a ProseMirror document as
// Markdown/CommonMark text.
export class MarkdownSerializer {
  // :: (Object<(state: MarkdownSerializerState, node: Node, parent: Node, index: number)>, Object)

  // Construct a serializer with the given configuration. The `nodes`
  // object should map node names in a given schema to function that
  // take a serializer state and such a node, and serialize the node.
  //
  // The `marks` object should hold objects with `open` and `close`
  // properties, which hold the strings that should appear before and
  // after a piece of text marked that way, either directly or as a
  // function that takes a serializer state and a mark, and returns a
  // string.
  //
  // Mark information objects can also have a `mixable` property
  // which, when `true`, indicates that the order in which the mark's
  // opening and closing syntax appears relative to other mixable
  // marks can be varied. (For example, you can say `**a *b***` and
  // `*a **b***`, but not `` `a *b*` ``.)
  //
  // The `expelEnclosingWhitespace` mark property causes the
  // serializer to move enclosing whitespace from inside the marks to
  // outside the marks. This is necessary for emphasis marks as
  // CommonMark does not permit enclosing whitespace inside emphasis
  // marks, see: http://spec.commonmark.org/0.26/#example-330
  constructor(nodes, marks) {
    // :: Object<(MarkdownSerializerState, Node)> The node serializer
    // functions for this serializer.
    this.nodes = nodes
    // :: Object The mark serializer info.
    this.marks = marks
  }

  // :: (Node, ?Object) → string
  // Serialize the content of the given node to
  // [CommonMark](http://commonmark.org/).
  serialize(content, options) {
    let state = new MarkdownSerializerState(this.nodes, this.marks, options)
    state.renderContent(content)
    return state.out
  }
}

// :: MarkdownSerializer
// A serializer for the [basic schema](#schema).
export const defaultMarkdownSerializer = new MarkdownSerializer({
  blockquote(state, node) {
    state.wrapBlock("> ", null, node, () => state.renderContent(node))
  },
  code_block(state, node) {
    state.write("```" + node.attrs.params + "\n")
    state.text(node.textContent, false)
    state.ensureNewLine()
    state.write("```")
    state.closeBlock(node)
  },
  heading(state, node) {
    state.write(state.repeat("#", node.attrs.level) + " ")
    state.renderInline(node)
    state.closeBlock(node)
  },
  horizontal_rule(state, node) {
    state.write(node.attrs.markup || "---")
    state.closeBlock(node)
  },
  bullet_list(state, node) {
    state.renderList(node, "  ", () => (node.attrs.bullet || "*") + " ")
  },
  ordered_list(state, node) {
    let start = node.attrs.order || 1
    let maxW = String(start + node.childCount - 1).length
    let space = state.repeat(" ", maxW + 2)
    state.renderList(node, space, i => {
      let nStr = String(start + i)
      return state.repeat(" ", maxW - nStr.length) + nStr + ". "
    })
  },
  list_item(state, node) {
    state.renderContent(node)
  },
  paragraph(state, node) {
    state.renderInline(node)
    state.closeBlock(node)
  },

  image(state, node) {
    state.write("![" + state.esc(node.attrs.alt || "") + "](" + state.esc(node.attrs.src) +
                (node.attrs.title ? " " + state.quote(node.attrs.title) : "") + ")")
  },
  hard_break(state, node, parent, index) {
    for (let i = index + 1; i < parent.childCount; i++)
      if (parent.child(i).type != node.type) {
        state.write("\\\n")
        return
      }
  },
  text(state, node) {
    state.text(node.text)
  }
}, {
  em: {open: "*", close: "*", mixable: true, expelEnclosingWhitespace: true},
  strong: {open: "**", close: "**", mixable: true, expelEnclosingWhitespace: true},
  link: {
    open: "[",
    close(state, mark) {
      return "](" + state.esc(mark.attrs.href) + (mark.attrs.title ? " " + state.quote(mark.attrs.title) : "") + ")"
    }
  },
  code: {open: "`", close: "`"}
})

// ::- This is an object used to track state and expose
// methods related to markdown serialization. Instances are passed to
// node and mark serialization methods (see `toMarkdown`).
export class MarkdownSerializerState {
  constructor(nodes, marks, options) {
    this.nodes = nodes
    this.marks = marks
    this.delim = this.out = ""
    this.closed = false
    this.inTightList = false
    // :: Object
    // The options passed to the serializer.
    //   tightLists:: ?bool
    //   Whether to render lists in a tight style. This can be overridden
    //   on a node level by specifying a tight attribute on the node.
    //   Defaults to false.
    this.options = options || {}
    if (typeof this.options.tightLists == "undefined")
      this.options.tightLists = false
  }

  flushClose(size) {
    if (this.closed) {
      if (!this.atBlank()) this.out += "\n"
      if (size == null) size = 2
      if (size > 1) {
        let delimMin = this.delim
        let trim = /\s+$/.exec(delimMin)
        if (trim) delimMin = delimMin.slice(0, delimMin.length - trim[0].length)
        for (let i = 1; i < size; i++)
          this.out += delimMin + "\n"
      }
      this.closed = false
    }
  }

  // :: (string, ?string, Node, ())
  // Render a block, prefixing each line with `delim`, and the first
  // line in `firstDelim`. `node` should be the node that is closed at
  // the end of the block, and `f` is a function that renders the
  // content of the block.
  wrapBlock(delim, firstDelim, node, f) {
    let old = this.delim
    this.write(firstDelim || delim)
    this.delim += delim
    f()
    this.delim = old
    this.closeBlock(node)
  }

  atBlank() {
    return /(^|\n)$/.test(this.out)
  }

  // :: ()
  // Ensure the current content ends with a newline.
  ensureNewLine() {
    if (!this.atBlank()) this.out += "\n"
  }

  // :: (?string)
  // Prepare the state for writing output (closing closed paragraphs,
  // adding delimiters, and so on), and then optionally add content
  // (unescaped) to the output.
  write(content) {
    this.flushClose()
    if (this.delim && this.atBlank())
      this.out += this.delim
    if (content) this.out += content
  }

  // :: (Node)
  // Close the block for the given node.
  closeBlock(node) {
    this.closed = node
  }

  // :: (string, ?bool)
  // Add the given text to the document. When escape is not `false`,
  // it will be escaped.
  text(text, escape) {
    let lines = text.split("\n")
    for (let i = 0; i < lines.length; i++) {
      var startOfLine = this.atBlank() || this.closed
      this.write()
      this.out += escape !== false ? this.esc(lines[i], startOfLine) : lines[i]
      if (i != lines.length - 1) this.out += "\n"
    }
  }

  // :: (Node)
  // Render the given node as a block.
  render(node, parent, index) {
    if (typeof parent == "number") throw new Error("!")
    this.nodes[node.type.name](this, node, parent, index)
  }

  // :: (Node)
  // Render the contents of `parent` as block nodes.
  renderContent(parent) {
    parent.forEach((node, _, i) => this.render(node, parent, i))
  }

  // :: (Node)
  // Render the contents of `parent` as inline content.
  renderInline(parent) {
    let active = [], trailing = ""
    let progress = (node, _, index) => {
      let marks = node ? node.marks : []

      let leading = trailing
      trailing = ""
      // If whitespace has to be expelled from the node, adjust
      // leading and trailing accordingly.
      if (node && node.isText && marks.some(mark => {
        let info = this.marks[mark.type.name]
        return info && info.expelEnclosingWhitespace
      })) {
        let [_, lead, inner, trail] = /^(\s*)(.*?)(\s*)$/m.exec(node.text)
        leading += lead
        trailing = trail
        if (lead || trail) {
          node = inner ? node.withText(inner) : null
          if (!node) marks = active
        }
      }

      let code = marks.length && marks[marks.length - 1].type.isCode && marks[marks.length - 1]
      let len = marks.length - (code ? 1 : 0)

      // Try to reorder 'mixable' marks, such as em and strong, which
      // in Markdown may be opened and closed in different order, so
      // that order of the marks for the token matches the order in
      // active.
      outer: for (let i = 0; i < len; i++) {
        let mark = marks[i]
        if (!this.marks[mark.type.name].mixable) break
        for (let j = 0; j < active.length; j++) {
          let other = active[j]
          if (!this.marks[other.type.name].mixable) break
          if (mark.eq(other)) {
            if (i > j)
              marks = marks.slice(0, j).concat(mark).concat(marks.slice(j, i)).concat(marks.slice(i + 1, len))
            else if (j > i)
              marks = marks.slice(0, i).concat(marks.slice(i + 1, j)).concat(mark).concat(marks.slice(j, len))
            continue outer
          }
        }
      }

      // Find the prefix of the mark set that didn't change
      let keep = 0
      while (keep < Math.min(active.length, len) && marks[keep].eq(active[keep])) ++keep

      // Close the marks that need to be closed
      while (keep < active.length)
        this.text(this.markString(active.pop(), false), false)

      // Output any previously expelled trailing whitespace outside the marks
      if (leading) this.text(leading)

      // Open the marks that need to be opened
      if (node) {
        while (active.length < len) {
          let add = marks[active.length]
          active.push(add)
          this.text(this.markString(add, true), false)
        }

        // Render the node. Special case code marks, since their content
        // may not be escaped.
        if (code && node.isText)
          this.text(this.markString(code, false) + node.text + this.markString(code, true), false)
        else
          this.render(node, parent, index)
      }
    }
    parent.forEach(progress)
    progress(null)
  }

  // :: (Node, string, (number) → string)
  // Render a node's content as a list. `delim` should be the extra
  // indentation added to all lines except the first in an item,
  // `firstDelim` is a function going from an item index to a
  // delimiter for the first line of the item.
  renderList(node, delim, firstDelim) {
    if (this.closed && this.closed.type == node.type)
      this.flushClose(3)
    else if (this.inTightList)
      this.flushClose(1)

    let isTight = typeof node.attrs.tight != "undefined" ? node.attrs.tight : this.options.tightLists
    let prevTight = this.inTightList
    this.inTightList = isTight
    node.forEach((child, _, i) => {
      if (i && isTight) this.flushClose(1)
      this.wrapBlock(delim, firstDelim(i), node, () => this.render(child, node, i))
    })
    this.inTightList = prevTight
  }

  // :: (string, ?bool) → string
  // Escape the given string so that it can safely appear in Markdown
  // content. If `startOfLine` is true, also escape characters that
  // has special meaning only at the start of the line.
  esc(str, startOfLine) {
    str = str.replace(/[`*\\~\[\]]/g, "\\$&")
    if (startOfLine) str = str.replace(/^[:#\-*+]/, "\\$&").replace(/^(\d+)\./, "$1\\.")
    return str
  }

  quote(str) {
    var wrap = str.indexOf('"') == -1 ? '""' : str.indexOf("'") == -1 ? "''" : "()"
    return wrap[0] + str + wrap[1]
  }

  // :: (string, number) → string
  // Repeat the given string `n` times.
  repeat(str, n) {
    let out = ""
    for (let i = 0; i < n; i++) out += str
    return out
  }

  // : (Mark, bool) → string
  // Get the markdown string for a given opening or closing mark.
  markString(mark, open) {
    let info = this.marks[mark.type.name]
    let value = open ? info.open : info.close
    return typeof value == "string" ? value : value(this, mark)
  }

  // :: (string) → { leading: ?string, trailing: ?string }
  // Get leading and trailing whitespace from a string. Values of
  // leading or trailing property of the return object will be undefined
  // if there is no match.
  getEnclosingWhitespace(text) {
    return {
      leading: (text.match(/^(\s+)/) || [])[0],
      trailing: (text.match(/(\s+)$/) || [])[0]
    }
  }

}
<|endoftext|>


<commit_before>import TOOL_TYPES from './types';

const { external, api, dep, ci, cli, framework } = TOOL_TYPES;

export default {
  accessibility: {
    title: 'Accessibility',
    subtitle:
      'Visualize, audit and test accessibility parameters in your project',
    tools: [
      {
        description: 'An accessibility visualization toolkit',
        link: 'https://github.com/Khan/tota11y',
        title: 'tota11y',
        type: [dep]
      },
      {
        description:
          "CSS file that provides warnings for possible risks and mistakes and can help evaluate a site's quality",
        link: 'https://github.com/ffoodd/a11y.css',
        title: 'a11y.css',
        type: [dep]
      },
      {
        description: 'Color combination contrast tester',
        link: 'https://colorable.jxnblk.com/',
        title: 'Colorable',
        type: [external]
      },
      {
        description: 'Automated accessibility testing using HTML CodeSniffer',
        link: 'https://github.com/pa11y/pa11y',
        title: 'Pa11y',
        type: [dep, cli]
      },
      {
        description:
          'Warns about potential accessibility issues with your React elements',
        link: 'https://github.com/reactjs/react-a11y',
        title: 'React A11y',
        type: [dep, framework]
      },
      {
        description:
          'A library of accessibility-related testing and utility code',
        link: 'https://github.com/GoogleChrome/accessibility-developer-tools',
        title: 'Accessibility Developer Tools',
        type: [dep]
      },
      {
        description:
          'An automated accessibility testing tool which crawls and tests pages to produce detailed reports',
        link: 'https://github.com/liip/TheA11yMachine',
        title: 'TheA11yMachine',
        type: [cli]
      }
    ]
  },
  auditing: {
    title: 'Performance auditing',
    subtitle: 'Audit and test the performance of your web page',
    tools: [
      {
        description:
          'Run tests on actual devices with different locations, browsers, and connection speeds',
        link: 'https://www.webpagetest.org/',
        title: 'WebPageTest',
        type: [external, api]
      },
      {
        description:
          'An open-source auditing tool that you can use to test and improve your webpage',
        link: 'https://developers.google.com/web/tools/lighthouse/',
        title: 'Lighthouse',
        type: [external]
      },
      {
        description:
          'Track and monitor performance of webpages during ongoing development',
        link: 'https://calibreapp.com/',
        title: 'Calibre',
        type: [external]
      },
      {
        description:
          'A devtool for inspecting the performance of React Components',
        link: 'https://github.com/nitin42/react-perf-devtool',
        title: 'React Performance Devtool',
        type: [external, framework]
      },
      {
        description:
          'Monitor web performance and how real users experience your site',
        link: 'https://speedcurve.com/',
        title: 'Speedcurve',
        type: [external]
      },
      {
        description:
          'Service by Netlify that rates your site based on its initial global load times and its security settings',
        link: 'https://testmysite.io/',
        title: 'Testmysite.io',
        type: [external]
      },
      {
        description:
          'An easy way to measure site performances across devices powered by WebPageTest',
        link: 'https://testmysite.withgoogle.com/',
        title: 'Test My Site',
        type: [external]
      },
      {
        description:
          'Get speed field data and suggestions for optimization improvements',
        link: 'https://developers.google.com/speed/pagespeed/insights/',
        title: 'PageSpeed Insights',
        type: [external]
      },
      {
        description:
          'Compare your mobile site speed with 10 other domains as well as estimate your revenue impact',
        link: 'https://www.thinkwithgoogle.com/feature/mobile/',
        title: 'Speed Scorecard',
        type: [external]
      },
      {
        description:
          'Generate a node map visualization of all third-party interactions',
        link: 'http://requestmap.webperf.tools/',
        title: 'Request Map Generator',
        type: [external]
      },
      {
        description:
          'Linting tool that will help your site with accessibility, speed, security and more, by checking your code for best practices and common errors',
        link: 'https://webhint.io/',
        title: 'webhint',
        type: [external, cli]
      }
    ]
  },
  'bundle-analysis': {
    title: 'Bundle analysis',
    subtitle: 'Analyze and measure your bundle and package sizes',
    tools: [
      {
        description: 'Find the cost of adding a npm package to your bundle',
        link: 'https://bundlephobia.com/',
        title: 'Bundlephobia',
        type: [external]
      },
      {
        description:
          'Visual Studio Code extension that displays the size of imported packages inline',
        link:
          'https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost',
        title: 'Import Cost',
        type: [external]
      },
      {
        description:
          'Visualize the size of webpack output files with a treemap',
        link: 'https://www.npmjs.com/package/webpack-bundle-analyzer',
        title: 'Webpack Bundle Analyzer',
        type: [dep]
      },
      {
        description: 'Visualize the size of parcel output files with a treemap',
        link:
          'https://github.com/gregtillbrook/parcel-plugin-bundle-visualiser',
        title: 'Parcel Plugin Bundle Analyzer',
        type: [dep]
      },
      {
        description: 'CLI dashboard for your webpack dev server',
        link: 'https://github.com/FormidableLabs/webpack-dashboard',
        title: 'Webpack Dashboard',
        type: [cli]
      },
      {
        description: 'Electron Desktop GUI for Webpack Dashboard',
        link: 'https://github.com/FormidableLabs/electron-webpack-dashboard',
        title: 'Electron Webpack Dashboard',
        type: [cli]
      },
      {
        description: 'An intelligent browser based Webpack dashboard',
        link: 'https://github.com/zouhir/jarvis',
        title: 'Jarvis',
        type: [dep]
      },
      {
        description: 'An online visualization tool for webpack bundles',
        link: 'https://chrisbateman.github.io/webpack-visualizer',
        title: 'Webpack Visualizer',
        type: [external]
      },
      {
        description: 'Analyze and debug code bloat through source maps',
        link: 'https://github.com/danvk/source-map-explorer',
        title: 'Source Map Explorer',
        type: [cli]
      },
      {
        description:
          'Tool to help you find source code duplication across your javascript chunks/splits',
        link: 'https://github.com/samccone/bundle-buddy',
        title: 'Bundle Buddy',
        type: [cli]
      },
      {
        description:
          'Tool for monitoring webpack optimization metrics through the development process',
        link: 'https://github.com/webpackmonitor/webpackmonitor',
        title: 'Webpack Monitor',
        type: [dep]
      },
      {
        description: 'Interactive pie chart for webpack stats',
        link: 'https://github.com/alexkuz/webpack-chart',
        title: 'Webpack Chart',
        type: [external]
      }
    ]
  },
  'bundle-budgets': {
    title: 'Bundle budgeting',
    subtitle: 'Set up budgets to keep your bundle sizes in check',
    tools: [
      {
        description: 'Keep an eye on your bundle size through a CI hook',
        link: 'https://github.com/siddharthkp/bundlesize',
        title: 'Bundlesize',
        type: [dep, ci]
      },
      {
        description:
          'Prevent library bloat by having error-checks when including large dependencies',
        link: 'https://github.com/ai/size-limit',
        title: 'size-limit',
        type: [dep]
      },
      {
        description:
          'Automatically track the size of your build artifacts with a CircleCI integration',
        link: 'https://buildsize.org/',
        title: 'BuildSize',
        type: [ci]
      }
    ]
  },
  'image-optimization': {
    title: 'Image optimization',
    subtitle: 'Optimize and compress images',
    tools: [
      {
        description: 'Minify images seamlessly',
        link: 'https://github.com/imagemin/imagemin',
        title: 'ImageMin',
        type: [dep, cli, external]
      },
      {
        description:
          'Reduce JPEG file sizes while retaining quality and compatibility',
        link: 'https://github.com/mozilla/mozjpeg',
        title: 'MozJPEG',
        type: [dep]
      },
      {
        description:
          'Save disk space & bandwidth by compressing images without losing quality',
        link: 'https://imageoptim.com/api',
        title: 'ImageOptim',
        type: [api, external]
      },
      {
        description:
          'A GUI tool for losslessly optimizing PNG and JPG files',
        link: 'https://trimage.org/',
        title: 'Trimage',
        type: [cli, external]
      }
    ]
  },
  'lazy-loading': {
    title: 'Lazy loading',
    subtitle:
      'Asynchronously load elements (DOM nodes, images, components or routes) when needed',
    tools: [
      {
        description:
          'Higher order component for loading components with dynamic imports',
        link: 'https://github.com/jamiebuilds/react-loadable',
        title: 'React Loadable',
        type: [dep]
      },
      {
        description: 'A fast, flexible, and small image lazy loader',
        link: 'https://github.com/malchata/yall.js',
        title: 'yall.js',
        type: [dep]
      },
      {
        description:
          'High performance and SEO friendly lazy loader for images, iframes and more',
        link: 'https://github.com/aFarkas/lazysizes',
        title: 'Lazysizes',
        type: [dep]
      },
      {
        description:
          'Detect if a DOM element intersects with another element such as the document viewport',
        link:
          'https://developers.google.com/web/updates/2016/04/intersectionobserver',
        title: 'Intersection Observer',
        type: [api]
      },
      {
        description:
          'Split bundles for lazy loading using entry points or dynamic imports',
        link: 'https://webpack.js.org/guides/code-splitting/',
        title: 'webpack',
        type: [dep]
      },
      {
        description:
          'Use dynamic imports to split applicatin code into separate bundles',
        link: 'https://parceljs.org/code_splitting.html',
        title: 'Parcel',
        type: [dep]
      }
    ]
  },
  'service-worker': {
    title: 'Service Worker',
    subtitle:
      'Cache static or dynamic assets for offline support using service workers',
    tools: [
      {
        description: 'Set of tools that simplify process of caching assets',
        link: 'https://developers.google.com/web/tools/workbox/',
        title: 'Workbox',
        type: [dep, cli]
      },
      {
        description:
          "Tiny JavaScript library that makes sure your users can always access your site's content",
        link: 'https://github.com/TalAter/UpUp',
        title: 'UpUp',
        type: [dep]
      },
      {
        description:
          'A webpack plugin that uses ServiceWorker (and AppCache) to provide an offline experience',
        link: 'https://github.com/NekR/offline-plugin',
        title: 'Offline Plugin',
        type: [dep]
      },
      {
        description:
          'Node module to generate a ServiceWorker that will precache specific resources',
        link: 'https://github.com/GoogleChromeLabs/sw-precache',
        title: 'Service Worker Precache',
        type: [dep, cli]
      },
      {
        description:
          'Collection of service worker tools for offlining runtime requests',
        link: 'https://github.com/GoogleChromeLabs/sw-toolbox',
        title: 'Service Worker Toolbox',
        type: [dep]
      }
    ]
  },
  fouc: {
    title: 'Flash of invisible/unstyled text',
    subtitle:
      'Tools to minimize unstyled/invisible text rendered to the web page while external styles are still being fetched',
    tools: [
      {
        description:
          'Minimize flash of unstyled text by comparing between a fallback and an intended font',
        link: 'https://meowni.ca/font-style-matcher/',
        title: 'Font style matcher',
        type: [external]
      },
      {
        description:
          'Track download progress and define lazy loading behavior of font faces',
        link:
          'https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/webfont-optimization#the_font_loading_api',
        title: 'CSS Font Loading',
        type: [api]
      },
      {
        description:
          'Control how a font is displayed while it is still being fetched',
        link: 'https://developers.google.com/web/updates/2016/02/font-display',
        title: 'font-display',
        type: [api]
      }
    ]
  },
  ssr: {
    title: 'Server-side rendering/pre-rendering',
    subtitle: 'Send down static HTML to improve perceived load times and SEO',
    tools: [
      {
        description: 'Universal javascript support for Angular',
        link: 'https://github.com/angular/universal',
        title: 'Angular Universal',
        type: [cli]
      },
      {
        description:
          'Zero-configuration framework-agnostic static prerendering for SPAs',
        link: 'https://github.com/stereobooster/react-snap',
        title: 'react-snap',
        type: [dep]
      },
      {
        description:
          'React.js server-side rendering optimization with component memoization and templatization',
        link: 'https://github.com/walmartlabs/react-ssr-optimization',
        title: 'react-ssr-optimization',
        type: [dep]
      },
      {
        description: 'Create a Preact PWA quickly with built-in pre-rendering',
        link: 'https://github.com/walmartlabs/react-ssr-optimization',
        title: 'preact-cli',
        type: [cli]
      },
      {
        description:
          'Add server-side rendering capabilities to a web server using Headless Chrome',
        link: 'https://developers.google.com/web/tools/puppeteer/articles/ssr',
        title: 'Puppeteer',
        type: [dep]
      }
    ]
  }
};
<commit_msg>webhint description shortened<commit_after>import TOOL_TYPES from './types';

const { external, api, dep, ci, cli, framework } = TOOL_TYPES;

export default {
  accessibility: {
    title: 'Accessibility',
    subtitle:
      'Visualize, audit and test accessibility parameters in your project',
    tools: [
      {
        description: 'An accessibility visualization toolkit',
        link: 'https://github.com/Khan/tota11y',
        title: 'tota11y',
        type: [dep]
      },
      {
        description:
          "CSS file that provides warnings for possible risks and mistakes and can help evaluate a site's quality",
        link: 'https://github.com/ffoodd/a11y.css',
        title: 'a11y.css',
        type: [dep]
      },
      {
        description: 'Color combination contrast tester',
        link: 'https://colorable.jxnblk.com/',
        title: 'Colorable',
        type: [external]
      },
      {
        description: 'Automated accessibility testing using HTML CodeSniffer',
        link: 'https://github.com/pa11y/pa11y',
        title: 'Pa11y',
        type: [dep, cli]
      },
      {
        description:
          'Warns about potential accessibility issues with your React elements',
        link: 'https://github.com/reactjs/react-a11y',
        title: 'React A11y',
        type: [dep, framework]
      },
      {
        description:
          'A library of accessibility-related testing and utility code',
        link: 'https://github.com/GoogleChrome/accessibility-developer-tools',
        title: 'Accessibility Developer Tools',
        type: [dep]
      },
      {
        description:
          'An automated accessibility testing tool which crawls and tests pages to produce detailed reports',
        link: 'https://github.com/liip/TheA11yMachine',
        title: 'TheA11yMachine',
        type: [cli]
      }
    ]
  },
  auditing: {
    title: 'Performance auditing',
    subtitle: 'Audit and test the performance of your web page',
    tools: [
      {
        description:
          'Run tests on actual devices with different locations, browsers, and connection speeds',
        link: 'https://www.webpagetest.org/',
        title: 'WebPageTest',
        type: [external, api]
      },
      {
        description:
          'An open-source auditing tool that you can use to test and improve your webpage',
        link: 'https://developers.google.com/web/tools/lighthouse/',
        title: 'Lighthouse',
        type: [external]
      },
      {
        description:
          'Track and monitor performance of webpages during ongoing development',
        link: 'https://calibreapp.com/',
        title: 'Calibre',
        type: [external]
      },
      {
        description:
          'A devtool for inspecting the performance of React Components',
        link: 'https://github.com/nitin42/react-perf-devtool',
        title: 'React Performance Devtool',
        type: [external, framework]
      },
      {
        description:
          'Monitor web performance and how real users experience your site',
        link: 'https://speedcurve.com/',
        title: 'Speedcurve',
        type: [external]
      },
      {
        description:
          'Service by Netlify that rates your site based on its initial global load times and its security settings',
        link: 'https://testmysite.io/',
        title: 'Testmysite.io',
        type: [external]
      },
      {
        description:
          'An easy way to measure site performances across devices powered by WebPageTest',
        link: 'https://testmysite.withgoogle.com/',
        title: 'Test My Site',
        type: [external]
      },
      {
        description:
          'Get speed field data and suggestions for optimization improvements',
        link: 'https://developers.google.com/speed/pagespeed/insights/',
        title: 'PageSpeed Insights',
        type: [external]
      },
      {
        description:
          'Compare your mobile site speed with 10 other domains as well as estimate your revenue impact',
        link: 'https://www.thinkwithgoogle.com/feature/mobile/',
        title: 'Speed Scorecard',
        type: [external]
      },
      {
        description:
          'Generate a node map visualization of all third-party interactions',
        link: 'http://requestmap.webperf.tools/',
        title: 'Request Map Generator',
        type: [external]
      },
      {
        description:
          'Linting tool that can help improve accessibility, speed and more by checking for best practices',
        link: 'https://webhint.io/',
        title: 'webhint',
        type: [external, cli]
      }
    ]
  },
  'bundle-analysis': {
    title: 'Bundle analysis',
    subtitle: 'Analyze and measure your bundle and package sizes',
    tools: [
      {
        description: 'Find the cost of adding a npm package to your bundle',
        link: 'https://bundlephobia.com/',
        title: 'Bundlephobia',
        type: [external]
      },
      {
        description:
          'Visual Studio Code extension that displays the size of imported packages inline',
        link:
          'https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost',
        title: 'Import Cost',
        type: [external]
      },
      {
        description:
          'Visualize the size of webpack output files with a treemap',
        link: 'https://www.npmjs.com/package/webpack-bundle-analyzer',
        title: 'Webpack Bundle Analyzer',
        type: [dep]
      },
      {
        description: 'Visualize the size of parcel output files with a treemap',
        link:
          'https://github.com/gregtillbrook/parcel-plugin-bundle-visualiser',
        title: 'Parcel Plugin Bundle Analyzer',
        type: [dep]
      },
      {
        description: 'CLI dashboard for your webpack dev server',
        link: 'https://github.com/FormidableLabs/webpack-dashboard',
        title: 'Webpack Dashboard',
        type: [cli]
      },
      {
        description: 'Electron Desktop GUI for Webpack Dashboard',
        link: 'https://github.com/FormidableLabs/electron-webpack-dashboard',
        title: 'Electron Webpack Dashboard',
        type: [cli]
      },
      {
        description: 'An intelligent browser based Webpack dashboard',
        link: 'https://github.com/zouhir/jarvis',
        title: 'Jarvis',
        type: [dep]
      },
      {
        description: 'An online visualization tool for webpack bundles',
        link: 'https://chrisbateman.github.io/webpack-visualizer',
        title: 'Webpack Visualizer',
        type: [external]
      },
      {
        description: 'Analyze and debug code bloat through source maps',
        link: 'https://github.com/danvk/source-map-explorer',
        title: 'Source Map Explorer',
        type: [cli]
      },
      {
        description:
          'Tool to help you find source code duplication across your javascript chunks/splits',
        link: 'https://github.com/samccone/bundle-buddy',
        title: 'Bundle Buddy',
        type: [cli]
      },
      {
        description:
          'Tool for monitoring webpack optimization metrics through the development process',
        link: 'https://github.com/webpackmonitor/webpackmonitor',
        title: 'Webpack Monitor',
        type: [dep]
      },
      {
        description: 'Interactive pie chart for webpack stats',
        link: 'https://github.com/alexkuz/webpack-chart',
        title: 'Webpack Chart',
        type: [external]
      }
    ]
  },
  'bundle-budgets': {
    title: 'Bundle budgeting',
    subtitle: 'Set up budgets to keep your bundle sizes in check',
    tools: [
      {
        description: 'Keep an eye on your bundle size through a CI hook',
        link: 'https://github.com/siddharthkp/bundlesize',
        title: 'Bundlesize',
        type: [dep, ci]
      },
      {
        description:
          'Prevent library bloat by having error-checks when including large dependencies',
        link: 'https://github.com/ai/size-limit',
        title: 'size-limit',
        type: [dep]
      },
      {
        description:
          'Automatically track the size of your build artifacts with a CircleCI integration',
        link: 'https://buildsize.org/',
        title: 'BuildSize',
        type: [ci]
      }
    ]
  },
  'image-optimization': {
    title: 'Image optimization',
    subtitle: 'Optimize and compress images',
    tools: [
      {
        description: 'Minify images seamlessly',
        link: 'https://github.com/imagemin/imagemin',
        title: 'ImageMin',
        type: [dep, cli, external]
      },
      {
        description:
          'Reduce JPEG file sizes while retaining quality and compatibility',
        link: 'https://github.com/mozilla/mozjpeg',
        title: 'MozJPEG',
        type: [dep]
      },
      {
        description:
          'Save disk space & bandwidth by compressing images without losing quality',
        link: 'https://imageoptim.com/api',
        title: 'ImageOptim',
        type: [api, external]
      },
      {
        description:
          'A GUI tool for losslessly optimizing PNG and JPG files',
        link: 'https://trimage.org/',
        title: 'Trimage',
        type: [cli, external]
      }
    ]
  },
  'lazy-loading': {
    title: 'Lazy loading',
    subtitle:
      'Asynchronously load elements (DOM nodes, images, components or routes) when needed',
    tools: [
      {
        description:
          'Higher order component for loading components with dynamic imports',
        link: 'https://github.com/jamiebuilds/react-loadable',
        title: 'React Loadable',
        type: [dep]
      },
      {
        description: 'A fast, flexible, and small image lazy loader',
        link: 'https://github.com/malchata/yall.js',
        title: 'yall.js',
        type: [dep]
      },
      {
        description:
          'High performance and SEO friendly lazy loader for images, iframes and more',
        link: 'https://github.com/aFarkas/lazysizes',
        title: 'Lazysizes',
        type: [dep]
      },
      {
        description:
          'Detect if a DOM element intersects with another element such as the document viewport',
        link:
          'https://developers.google.com/web/updates/2016/04/intersectionobserver',
        title: 'Intersection Observer',
        type: [api]
      },
      {
        description:
          'Split bundles for lazy loading using entry points or dynamic imports',
        link: 'https://webpack.js.org/guides/code-splitting/',
        title: 'webpack',
        type: [dep]
      },
      {
        description:
          'Use dynamic imports to split applicatin code into separate bundles',
        link: 'https://parceljs.org/code_splitting.html',
        title: 'Parcel',
        type: [dep]
      }
    ]
  },
  'service-worker': {
    title: 'Service Worker',
    subtitle:
      'Cache static or dynamic assets for offline support using service workers',
    tools: [
      {
        description: 'Set of tools that simplify process of caching assets',
        link: 'https://developers.google.com/web/tools/workbox/',
        title: 'Workbox',
        type: [dep, cli]
      },
      {
        description:
          "Tiny JavaScript library that makes sure your users can always access your site's content",
        link: 'https://github.com/TalAter/UpUp',
        title: 'UpUp',
        type: [dep]
      },
      {
        description:
          'A webpack plugin that uses ServiceWorker (and AppCache) to provide an offline experience',
        link: 'https://github.com/NekR/offline-plugin',
        title: 'Offline Plugin',
        type: [dep]
      },
      {
        description:
          'Node module to generate a ServiceWorker that will precache specific resources',
        link: 'https://github.com/GoogleChromeLabs/sw-precache',
        title: 'Service Worker Precache',
        type: [dep, cli]
      },
      {
        description:
          'Collection of service worker tools for offlining runtime requests',
        link: 'https://github.com/GoogleChromeLabs/sw-toolbox',
        title: 'Service Worker Toolbox',
        type: [dep]
      }
    ]
  },
  fouc: {
    title: 'Flash of invisible/unstyled text',
    subtitle:
      'Tools to minimize unstyled/invisible text rendered to the web page while external styles are still being fetched',
    tools: [
      {
        description:
          'Minimize flash of unstyled text by comparing between a fallback and an intended font',
        link: 'https://meowni.ca/font-style-matcher/',
        title: 'Font style matcher',
        type: [external]
      },
      {
        description:
          'Track download progress and define lazy loading behavior of font faces',
        link:
          'https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/webfont-optimization#the_font_loading_api',
        title: 'CSS Font Loading',
        type: [api]
      },
      {
        description:
          'Control how a font is displayed while it is still being fetched',
        link: 'https://developers.google.com/web/updates/2016/02/font-display',
        title: 'font-display',
        type: [api]
      }
    ]
  },
  ssr: {
    title: 'Server-side rendering/pre-rendering',
    subtitle: 'Send down static HTML to improve perceived load times and SEO',
    tools: [
      {
        description: 'Universal javascript support for Angular',
        link: 'https://github.com/angular/universal',
        title: 'Angular Universal',
        type: [cli]
      },
      {
        description:
          'Zero-configuration framework-agnostic static prerendering for SPAs',
        link: 'https://github.com/stereobooster/react-snap',
        title: 'react-snap',
        type: [dep]
      },
      {
        description:
          'React.js server-side rendering optimization with component memoization and templatization',
        link: 'https://github.com/walmartlabs/react-ssr-optimization',
        title: 'react-ssr-optimization',
        type: [dep]
      },
      {
        description: 'Create a Preact PWA quickly with built-in pre-rendering',
        link: 'https://github.com/walmartlabs/react-ssr-optimization',
        title: 'preact-cli',
        type: [cli]
      },
      {
        description:
          'Add server-side rendering capabilities to a web server using Headless Chrome',
        link: 'https://developers.google.com/web/tools/puppeteer/articles/ssr',
        title: 'Puppeteer',
        type: [dep]
      }
    ]
  }
};
<|endoftext|>


<commit_before>// 80: Number - isNaN
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!

describe('`Number.isNaN()` determines if a value is `NaN`', function(){
  it('it is a static function on `Number`', function() {
    //// const whatType = 'method';
    const whatType = 'function';
    assert.equal(typeof Number.isNaN, whatType);
  });
  describe('returns false', () => {
    describe('for any not-Number type', () => {
      it('like null', () => {
        assert.equal(Number.isNaN(null), false);
      });
      it('like a string', () => {
        assert.equal(Number.isNaN('a string'), false);
      });
      it('like an object', () => {
        assert.equal(Number.isNaN({}), false);
      });
      describe('different to the global `isNaN` function (specified way before ES6)', () => {
        it('e.g. an object gets converted to type Number before the check, and returns true therefore', () => {
          //// const fn = Number.isNaN;
          const fn = isNaN;
          assert.equal(fn({}), true);
        });
        it('also a string gets converted to type Number first, and returns true therefore (even though its not `NaN`)', () => {
          //// const fn = Number.isNaN;
          const fn = isNaN;
          assert.equal(fn('just a string'), true);
        });
      });
    });
    describe('for Numbers', () => {
      it('like 0', () => {
        assert.equal(Number.isNaN(NaN), true);
      });
    });
  });
  describe('returns true for', () => {
    it('exactly `NaN`', () => {
      //// const notANumber = 1;
      const notANumber = NaN;
      assert.equal(Number.isNaN(notANumber), true);
    });
    it('zero divided by zero', () => {
      //// const zeroDividedByZero = 1 / 2;
      const zeroDividedByZero = 0 / 0;
      assert.equal(Number.isNaN(zeroDividedByZero), true);
    });
  });
});
<commit_msg>Kataify some.<commit_after>// 80: Number - isNaN
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!

describe('`Number.isNaN()` determines if a value is `NaN`', function(){
  it('it is a static function on `Number`', function() {
    //// const whatType = 'method';
    const whatType = 'function';
    assert.equal(typeof Number.isNaN, whatType);
  });
  describe('returns false', () => {
    describe('for any not-Number type', () => {
      it('like null', () => {
        //// const justNull = NaN;
        const justNull = null;
        assert.equal(Number.isNaN(justNull), false);
      });
      it('like a string', () => {
        //// const aString = NaN;
        const aString = 'NaN';
        assert.equal(Number.isNaN(aString), false);
      });
      it('like an object', () => {
        //// const anObject = NaN;
        const anObject = {};
        assert.equal(Number.isNaN(anObject), false);
      });
      describe('different to the global `isNaN` function (specified way before ES6)', () => {
        it('e.g. an object gets converted to a Number before the check, and returns true therefore', () => {
          //// const fn = Number.isNaN;
          const fn = isNaN;
          assert.equal(fn({}), true);
        });
        it('also a string gets converted to a Number first, and returns true therefore (even though its not `NaN`)', () => {
          //// const fn = Number.isNaN;
          const fn = isNaN;
          assert.equal(fn('just a string'), true);
        });
      });
    });
    describe('for Numbers', () => {
      it('like 0', () => {
        assert.equal(Number.isNaN(NaN), true);
      });
    });
  });
  describe('returns true for', () => {
    it('exactly `NaN`', () => {
      //// const notANumber = 1;
      const notANumber = NaN;
      assert.equal(Number.isNaN(notANumber), true);
    });
    it('zero divided by zero', () => {
      //// const zeroDividedByZero = 1 / 2;
      const zeroDividedByZero = 0 / 0;
      assert.equal(Number.isNaN(zeroDividedByZero), true);
    });
  });
});
<|endoftext|>


<commit_before>var bcrypto = require('./crypto')
var bscript = require('./script')
var bufferutils = require('./bufferutils')
var bufferReverse = require('buffer-reverse')
var opcodes = require('./opcodes.json')
var typeforce = require('typeforce')
var types = require('./types')

function varSliceSize (someScript) {
  var length = someScript.length

  return bufferutils.varIntSize(length) + length
}

function vectorSize (someVector) {
  var length = someVector.length

  return bufferutils.varIntSize(length) + someVector.reduce(function (sum, witness) {
    return sum + varSliceSize(witness)
  }, 0)
}

function Transaction () {
  this.version = 1
  this.locktime = 0
  this.ins = []
  this.outs = []
}

Transaction.DEFAULT_SEQUENCE = 0xffffffff
Transaction.SIGHASH_ALL = 0x01
Transaction.SIGHASH_NONE = 0x02
Transaction.SIGHASH_SINGLE = 0x03
Transaction.SIGHASH_ANYONECANPAY = 0x80
Transaction.ADVANCED_TRANSACTION_MARKER = 0x00
Transaction.ADVANCED_TRANSACTION_FLAG = 0x01

var EMPTY_SCRIPT = new Buffer(0)
var EMPTY_WITNESS = []
var ZERO = new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
var ONE = new Buffer('0000000000000000000000000000000000000000000000000000000000000001', 'hex')
var VALUE_UINT64_MAX = new Buffer('ffffffffffffffff', 'hex')
var BLANK_OUTPUT = {
  script: EMPTY_SCRIPT,
  valueBuffer: VALUE_UINT64_MAX
}

Transaction.fromBuffer = function (buffer, __noStrict) {
  var offset = 0
  function readSlice (n) {
    offset += n
    return buffer.slice(offset - n, offset)
  }

  function readUInt32 () {
    var i = buffer.readUInt32LE(offset)
    offset += 4
    return i
  }

  function readInt32 () {
    var i = buffer.readInt32LE(offset)
    offset += 4
    return i
  }

  function readUInt64 () {
    var i = bufferutils.readUInt64LE(buffer, offset)
    offset += 8
    return i
  }

  function readVarInt () {
    var vi = bufferutils.readVarInt(buffer, offset)
    offset += vi.size
    return vi.number
  }

  function readVarSlice () {
    return readSlice(readVarInt())
  }

  function readVector () {
    var count = readVarInt()
    var vector = []
    for (var i = 0; i < count; i++) vector.push(readVarSlice())
    return vector
  }

  var tx = new Transaction()
  tx.version = readInt32()

  var marker = buffer.readUInt8(offset)
  var flag = buffer.readUInt8(offset + 1)

  var hasWitnesses = false
  if (marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
      flag === Transaction.ADVANCED_TRANSACTION_FLAG) {
    offset += 2
    hasWitnesses = true
  }

  var vinLen = readVarInt()
  for (var i = 0; i < vinLen; ++i) {
    tx.ins.push({
      hash: readSlice(32),
      index: readUInt32(),
      script: readVarSlice(),
      sequence: readUInt32(),
      witness: EMPTY_WITNESS
    })
  }

  var voutLen = readVarInt()
  for (i = 0; i < voutLen; ++i) {
    tx.outs.push({
      value: readUInt64(),
      script: readVarSlice()
    })
  }

  if (hasWitnesses) {
    for (i = 0; i < vinLen; ++i) {
      tx.ins[i].witness = readVector()
    }

    // was this pointless?
    if (!tx._hasWitnesses()) throw new Error('Transaction has superfluous witness data')
  }

  tx.locktime = readUInt32()

  if (__noStrict) return tx
  if (offset !== buffer.length) throw new Error('Transaction has unexpected data')

  return tx
}

Transaction.fromHex = function (hex) {
  return Transaction.fromBuffer(new Buffer(hex, 'hex'))
}

Transaction.isCoinbaseHash = function (buffer) {
  typeforce(types.Hash256bit, buffer)
  for (var i = 0; i < 32; ++i) {
    if (buffer[i] !== 0) return false
  }
  return true
}

Transaction.prototype.isCoinbase = function () {
  return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash)
}

Transaction.prototype.addInput = function (hash, index, sequence, scriptSig) {
  typeforce(types.tuple(
    types.Hash256bit,
    types.UInt32,
    types.maybe(types.UInt32),
    types.maybe(types.Buffer)
  ), arguments)

  if (types.Null(sequence)) {
    sequence = Transaction.DEFAULT_SEQUENCE
  }

  // Add the input and return the input's index
  return (this.ins.push({
    hash: hash,
    index: index,
    script: scriptSig || EMPTY_SCRIPT,
    sequence: sequence,
    witness: EMPTY_WITNESS
  }) - 1)
}

Transaction.prototype.addOutput = function (scriptPubKey, value) {
  typeforce(types.tuple(types.Buffer, types.Satoshi), arguments)

  // Add the output and return the output's index
  return (this.outs.push({
    script: scriptPubKey,
    value: value
  }) - 1)
}

Transaction.prototype._hasWitnesses = function () {
  return this.ins.some(function (x) {
    return x.witness.length !== 0
  })
}

Transaction.prototype.byteLength = function () {
  var hasWitnesses = this._hasWitnesses()

  return (
    (hasWitnesses ? 10 : 8) +
    bufferutils.varIntSize(this.ins.length) +
    bufferutils.varIntSize(this.outs.length) +
    this.ins.reduce(function (sum, input) { return sum + 40 + varSliceSize(input.script) }, 0) +
    this.outs.reduce(function (sum, output) { return sum + 8 + varSliceSize(output.script) }, 0) +
    (hasWitnesses ? this.ins.reduce(function (sum, input) { return sum + vectorSize(input.witness) }, 0) : 0)
  )
}

Transaction.prototype.clone = function () {
  var newTx = new Transaction()
  newTx.version = this.version
  newTx.locktime = this.locktime

  newTx.ins = this.ins.map(function (txIn) {
    return {
      hash: txIn.hash,
      index: txIn.index,
      script: txIn.script,
      sequence: txIn.sequence,
      witness: txIn.witness
    }
  })

  newTx.outs = this.outs.map(function (txOut) {
    return {
      script: txOut.script,
      value: txOut.value
    }
  })

  return newTx
}

/**
 * Hash transaction for signing a specific input.
 *
 * Bitcoin uses a different hash for each signed transaction input.
 * This method copies the transaction, makes the necessary changes based on the
 * hashType, and then hashes the result.
 * This hash can then be used to sign the provided transaction input.
 */
Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashType) {
  typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments)

  // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
  if (inIndex >= this.ins.length) return ONE

  // ignore OP_CODESEPARATOR
  var ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(function (x) {
    return x !== opcodes.OP_CODESEPARATOR
  }))

  var txTmp = this.clone()

  // SIGHASH_NONE: ignore all outputs? (wildcard payee)
  if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) {
    txTmp.outs = []

    // ignore sequence numbers (except at inIndex)
    txTmp.ins.forEach(function (input, i) {
      if (i === inIndex) return

      input.sequence = 0
    })

  // SIGHASH_SINGLE: ignore all outputs, except at the same index?
  } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) {
    // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60
    if (inIndex >= this.outs.length) return ONE

    // truncate outputs after
    txTmp.outs.length = inIndex + 1

    // "blank" outputs before
    for (var i = 0; i < inIndex; i++) {
      txTmp.outs[i] = BLANK_OUTPUT
    }

    // ignore sequence numbers (except at inIndex)
    txTmp.ins.forEach(function (input, i) {
      if (i === inIndex) return

      input.sequence = 0
    })
  }

  // SIGHASH_ANYONECANPAY: ignore inputs entirely?
  if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
    txTmp.ins = [txTmp.ins[inIndex]]
    txTmp.ins[0].script = ourScript

  // SIGHASH_ALL: only ignore input scripts
  } else {
    // "blank" others input scripts
    txTmp.ins.forEach(function (input) { input.script = EMPTY_SCRIPT })
    txTmp.ins[inIndex].script = ourScript
  }

  // serialize and hash
  var buffer = new Buffer(txTmp.byteLength() + 4)
  buffer.writeInt32LE(hashType, buffer.length - 4)
  txTmp.toBuffer(buffer, 0)

  return bcrypto.hash256(buffer)
}

Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value, hashType) {
  typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments)

  var tbuffer, toffset
  function writeSlice (slice) { toffset += slice.copy(tbuffer, toffset) }
  function writeUInt32 (i) { toffset = tbuffer.writeUInt32LE(i, toffset) }
  function writeUInt64 (i) { toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset) }
  function writeVarInt (i) { toffset += bufferutils.writeVarInt(tbuffer, i, toffset) }
  function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) }

  var hashOutputs = ZERO
  var hashPrevouts = ZERO
  var hashSequence = ZERO

  if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) {
    tbuffer = new Buffer(36 * this.ins.length)
    toffset = 0

    this.ins.forEach(function (txIn) {
      writeSlice(txIn.hash)
      writeUInt32(txIn.index)
    })

    hashPrevouts = bcrypto.hash256(tbuffer)
  }

  if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) &&
       (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
       (hashType & 0x1f) !== Transaction.SIGHASH_NONE) {
    tbuffer = new Buffer(4 * this.ins.length)
    toffset = 0

    this.ins.forEach(function (txIn) {
      writeUInt32(txIn.sequence)
    })

    hashSequence = bcrypto.hash256(tbuffer)
  }

  if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
      (hashType & 0x1f) !== Transaction.SIGHASH_NONE) {
    var txOutsSize = this.outs.reduce(function (sum, output) {
      return sum + 8 + varSliceSize(output.script)
    }, 0)

    tbuffer = new Buffer(txOutsSize)
    toffset = 0

    this.outs.forEach(function (out) {
      writeUInt64(out.value)
      writeVarSlice(out.script)
    })

    hashOutputs = bcrypto.hash256(tbuffer)
  } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) {
    var output = this.outs[inIndex]

    tbuffer = new Buffer(8 + varSliceSize(output.script))
    toffset = 0
    writeUInt64(output.value)
    writeVarSlice(output.script)

    hashOutputs = bcrypto.hash256(tbuffer)
  }

  tbuffer = new Buffer(156 + varSliceSize(prevOutScript))
  toffset = 0

  var input = this.ins[inIndex]
  writeUInt32(this.version)
  writeSlice(hashPrevouts)
  writeSlice(hashSequence)
  writeSlice(input.hash)
  writeUInt32(input.index)
  writeVarSlice(prevOutScript)
  writeUInt64(value)
  writeUInt32(input.sequence)
  writeSlice(hashOutputs)
  writeUInt32(this.locktime)
  writeUInt32(hashType)

  return bcrypto.hash256(tbuffer)
}

Transaction.prototype.getHash = function () {
  return bcrypto.hash256(this.toBuffer())
}

Transaction.prototype.getId = function () {
  // transaction hash's are displayed in reverse order
  return bufferReverse(this.getHash()).toString('hex')
}

Transaction.prototype.toBuffer = function (buffer, initialOffset) {
  if (!buffer) buffer = new Buffer(this.byteLength())

  var offset = initialOffset || 0
  function writeSlice (slice) { offset += slice.copy(buffer, offset) }
  function writeUInt8 (i) { offset = buffer.writeUInt8(i, offset) }
  function writeUInt32 (i) { offset = buffer.writeUInt32LE(i, offset) }
  function writeInt32 (i) { offset = buffer.writeInt32LE(i, offset) }
  function writeUInt64 (i) { offset = bufferutils.writeUInt64LE(buffer, i, offset) }
  function writeVarInt (i) { offset += bufferutils.writeVarInt(buffer, i, offset) }
  function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) }
  function writeVector (vector) { writeVarInt(vector.length); vector.forEach(writeVarSlice) }

  writeInt32(this.version)

  var hasWitnesses = this._hasWitnesses()

  if (hasWitnesses) {
    writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER)
    writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG)
  }

  writeVarInt(this.ins.length)

  this.ins.forEach(function (txIn) {
    writeSlice(txIn.hash)
    writeUInt32(txIn.index)
    writeVarSlice(txIn.script)
    writeUInt32(txIn.sequence)
  })

  writeVarInt(this.outs.length)
  this.outs.forEach(function (txOut) {
    if (!txOut.valueBuffer) {
      writeUInt64(txOut.value)
    } else {
      writeSlice(txOut.valueBuffer)
    }

    writeVarSlice(txOut.script)
  })

  if (hasWitnesses) {
    this.ins.forEach(function (input) {
      writeVector(input.witness)
    })
  }

  writeUInt32(this.locktime)

  // avoid slicing unless necessary
  if (initialOffset !== undefined) return buffer.slice(initialOffset, offset)
  return buffer
}

Transaction.prototype.toHex = function () {
  return this.toBuffer().toString('hex')
}

Transaction.prototype.setInputScript = function (index, scriptSig) {
  typeforce(types.tuple(types.Number, types.Buffer), arguments)

  this.ins[index].script = scriptSig
}

Transaction.prototype.setWitness = function (index, witness) {
  typeforce(types.tuple(types.Number, [types.Buffer]), arguments)

  this.ins[index].witness = witness
}

module.exports = Transaction
<commit_msg>Transaction: Witness serialization support default only for some functions (fixed)<commit_after>var bcrypto = require('./crypto')
var bscript = require('./script')
var bufferutils = require('./bufferutils')
var bufferReverse = require('buffer-reverse')
var opcodes = require('./opcodes.json')
var typeforce = require('typeforce')
var types = require('./types')

function varSliceSize (someScript) {
  var length = someScript.length

  return bufferutils.varIntSize(length) + length
}

function vectorSize (someVector) {
  var length = someVector.length

  return bufferutils.varIntSize(length) + someVector.reduce(function (sum, witness) {
    return sum + varSliceSize(witness)
  }, 0)
}

function Transaction () {
  this.version = 1
  this.locktime = 0
  this.ins = []
  this.outs = []
}

Transaction.DEFAULT_SEQUENCE = 0xffffffff
Transaction.SIGHASH_ALL = 0x01
Transaction.SIGHASH_NONE = 0x02
Transaction.SIGHASH_SINGLE = 0x03
Transaction.SIGHASH_ANYONECANPAY = 0x80
Transaction.ADVANCED_TRANSACTION_MARKER = 0x00
Transaction.ADVANCED_TRANSACTION_FLAG = 0x01

var EMPTY_SCRIPT = new Buffer(0)
var EMPTY_WITNESS = []
var ZERO = new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
var ONE = new Buffer('0000000000000000000000000000000000000000000000000000000000000001', 'hex')
var VALUE_UINT64_MAX = new Buffer('ffffffffffffffff', 'hex')
var BLANK_OUTPUT = {
  script: EMPTY_SCRIPT,
  valueBuffer: VALUE_UINT64_MAX
}

Transaction.fromBuffer = function (buffer, __noStrict) {
  var offset = 0
  function readSlice (n) {
    offset += n
    return buffer.slice(offset - n, offset)
  }

  function readUInt32 () {
    var i = buffer.readUInt32LE(offset)
    offset += 4
    return i
  }

  function readInt32 () {
    var i = buffer.readInt32LE(offset)
    offset += 4
    return i
  }

  function readUInt64 () {
    var i = bufferutils.readUInt64LE(buffer, offset)
    offset += 8
    return i
  }

  function readVarInt () {
    var vi = bufferutils.readVarInt(buffer, offset)
    offset += vi.size
    return vi.number
  }

  function readVarSlice () {
    return readSlice(readVarInt())
  }

  function readVector () {
    var count = readVarInt()
    var vector = []
    for (var i = 0; i < count; i++) vector.push(readVarSlice())
    return vector
  }

  var tx = new Transaction()
  tx.version = readInt32()

  var marker = buffer.readUInt8(offset)
  var flag = buffer.readUInt8(offset + 1)

  var hasWitnesses = false
  if (marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
      flag === Transaction.ADVANCED_TRANSACTION_FLAG) {
    offset += 2
    hasWitnesses = true
  }

  var vinLen = readVarInt()
  for (var i = 0; i < vinLen; ++i) {
    tx.ins.push({
      hash: readSlice(32),
      index: readUInt32(),
      script: readVarSlice(),
      sequence: readUInt32(),
      witness: EMPTY_WITNESS
    })
  }

  var voutLen = readVarInt()
  for (i = 0; i < voutLen; ++i) {
    tx.outs.push({
      value: readUInt64(),
      script: readVarSlice()
    })
  }

  if (hasWitnesses) {
    for (i = 0; i < vinLen; ++i) {
      tx.ins[i].witness = readVector()
    }

    // was this pointless?
    if (!tx.__hasWitnesses()) throw new Error('Transaction has superfluous witness data')
  }

  tx.locktime = readUInt32()

  if (__noStrict) return tx
  if (offset !== buffer.length) throw new Error('Transaction has unexpected data')

  return tx
}

Transaction.fromHex = function (hex) {
  return Transaction.fromBuffer(new Buffer(hex, 'hex'))
}

Transaction.isCoinbaseHash = function (buffer) {
  typeforce(types.Hash256bit, buffer)
  for (var i = 0; i < 32; ++i) {
    if (buffer[i] !== 0) return false
  }
  return true
}

Transaction.prototype.isCoinbase = function () {
  return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash)
}

Transaction.prototype.addInput = function (hash, index, sequence, scriptSig) {
  typeforce(types.tuple(
    types.Hash256bit,
    types.UInt32,
    types.maybe(types.UInt32),
    types.maybe(types.Buffer)
  ), arguments)

  if (types.Null(sequence)) {
    sequence = Transaction.DEFAULT_SEQUENCE
  }

  // Add the input and return the input's index
  return (this.ins.push({
    hash: hash,
    index: index,
    script: scriptSig || EMPTY_SCRIPT,
    sequence: sequence,
    witness: EMPTY_WITNESS
  }) - 1)
}

Transaction.prototype.addOutput = function (scriptPubKey, value) {
  typeforce(types.tuple(types.Buffer, types.Satoshi), arguments)

  // Add the output and return the output's index
  return (this.outs.push({
    script: scriptPubKey,
    value: value
  }) - 1)
}

Transaction.prototype.__hasWitnesses = function () {
  return this.ins.some(function (x) {
    return x.witness.length !== 0
  })
}

Transaction.prototype.byteLength = function () {
  return this.__byteLength(true)
}

Transaction.prototype.__byteLength = function (__allowWitness) {
  var hasWitnesses = __allowWitness && this.__hasWitnesses()

  return (
    (hasWitnesses ? 10 : 8) +
    bufferutils.varIntSize(this.ins.length) +
    bufferutils.varIntSize(this.outs.length) +
    this.ins.reduce(function (sum, input) { return sum + 40 + varSliceSize(input.script) }, 0) +
    this.outs.reduce(function (sum, output) { return sum + 8 + varSliceSize(output.script) }, 0) +
    (hasWitnesses ? this.ins.reduce(function (sum, input) { return sum + vectorSize(input.witness) }, 0) : 0)
  )
}

Transaction.prototype.clone = function () {
  var newTx = new Transaction()
  newTx.version = this.version
  newTx.locktime = this.locktime

  newTx.ins = this.ins.map(function (txIn) {
    return {
      hash: txIn.hash,
      index: txIn.index,
      script: txIn.script,
      sequence: txIn.sequence,
      witness: txIn.witness
    }
  })

  newTx.outs = this.outs.map(function (txOut) {
    return {
      script: txOut.script,
      value: txOut.value
    }
  })

  return newTx
}

/**
 * Hash transaction for signing a specific input.
 *
 * Bitcoin uses a different hash for each signed transaction input.
 * This method copies the transaction, makes the necessary changes based on the
 * hashType, and then hashes the result.
 * This hash can then be used to sign the provided transaction input.
 */
Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashType) {
  typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments)

  // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
  if (inIndex >= this.ins.length) return ONE

  // ignore OP_CODESEPARATOR
  var ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(function (x) {
    return x !== opcodes.OP_CODESEPARATOR
  }))

  var txTmp = this.clone()

  // SIGHASH_NONE: ignore all outputs? (wildcard payee)
  if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) {
    txTmp.outs = []

    // ignore sequence numbers (except at inIndex)
    txTmp.ins.forEach(function (input, i) {
      if (i === inIndex) return

      input.sequence = 0
    })

  // SIGHASH_SINGLE: ignore all outputs, except at the same index?
  } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) {
    // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60
    if (inIndex >= this.outs.length) return ONE

    // truncate outputs after
    txTmp.outs.length = inIndex + 1

    // "blank" outputs before
    for (var i = 0; i < inIndex; i++) {
      txTmp.outs[i] = BLANK_OUTPUT
    }

    // ignore sequence numbers (except at inIndex)
    txTmp.ins.forEach(function (input, i) {
      if (i === inIndex) return

      input.sequence = 0
    })
  }

  // SIGHASH_ANYONECANPAY: ignore inputs entirely?
  if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
    txTmp.ins = [txTmp.ins[inIndex]]
    txTmp.ins[0].script = ourScript

  // SIGHASH_ALL: only ignore input scripts
  } else {
    // "blank" others input scripts
    txTmp.ins.forEach(function (input) { input.script = EMPTY_SCRIPT })
    txTmp.ins[inIndex].script = ourScript
  }

  // serialize and hash
  var buffer = new Buffer(txTmp.__byteLength(false) + 4)
  buffer.writeInt32LE(hashType, buffer.length - 4)
  txTmp.__toBuffer(buffer, 0, false)

  return bcrypto.hash256(buffer)
}

Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value, hashType) {
  typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments)

  var tbuffer, toffset
  function writeSlice (slice) { toffset += slice.copy(tbuffer, toffset) }
  function writeUInt32 (i) { toffset = tbuffer.writeUInt32LE(i, toffset) }
  function writeUInt64 (i) { toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset) }
  function writeVarInt (i) { toffset += bufferutils.writeVarInt(tbuffer, i, toffset) }
  function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) }

  var hashOutputs = ZERO
  var hashPrevouts = ZERO
  var hashSequence = ZERO

  if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) {
    tbuffer = new Buffer(36 * this.ins.length)
    toffset = 0

    this.ins.forEach(function (txIn) {
      writeSlice(txIn.hash)
      writeUInt32(txIn.index)
    })

    hashPrevouts = bcrypto.hash256(tbuffer)
  }

  if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) &&
       (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
       (hashType & 0x1f) !== Transaction.SIGHASH_NONE) {
    tbuffer = new Buffer(4 * this.ins.length)
    toffset = 0

    this.ins.forEach(function (txIn) {
      writeUInt32(txIn.sequence)
    })

    hashSequence = bcrypto.hash256(tbuffer)
  }

  if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
      (hashType & 0x1f) !== Transaction.SIGHASH_NONE) {
    var txOutsSize = this.outs.reduce(function (sum, output) {
      return sum + 8 + varSliceSize(output.script)
    }, 0)

    tbuffer = new Buffer(txOutsSize)
    toffset = 0

    this.outs.forEach(function (out) {
      writeUInt64(out.value)
      writeVarSlice(out.script)
    })

    hashOutputs = bcrypto.hash256(tbuffer)
  } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) {
    var output = this.outs[inIndex]

    tbuffer = new Buffer(8 + varSliceSize(output.script))
    toffset = 0
    writeUInt64(output.value)
    writeVarSlice(output.script)

    hashOutputs = bcrypto.hash256(tbuffer)
  }

  tbuffer = new Buffer(156 + varSliceSize(prevOutScript))
  toffset = 0

  var input = this.ins[inIndex]
  writeUInt32(this.version)
  writeSlice(hashPrevouts)
  writeSlice(hashSequence)
  writeSlice(input.hash)
  writeUInt32(input.index)
  writeVarSlice(prevOutScript)
  writeUInt64(value)
  writeUInt32(input.sequence)
  writeSlice(hashOutputs)
  writeUInt32(this.locktime)
  writeUInt32(hashType)

  return bcrypto.hash256(tbuffer)
}

Transaction.prototype.getHash = function () {
  return bcrypto.hash256(this.__toBuffer(undefined, undefined, false))
}

Transaction.prototype.getId = function () {
  // transaction hash's are displayed in reverse order
  return bufferReverse(this.getHash()).toString('hex')
}

Transaction.prototype.toBuffer = function (buffer, initialOffset) {
  return this.__toBuffer(buffer, initialOffset, true)
}

Transaction.prototype.__toBuffer = function (buffer, initialOffset, __allowWitness) {
  if (!buffer) buffer = new Buffer(this.__byteLength(__allowWitness))

  var offset = initialOffset || 0
  function writeSlice (slice) { offset += slice.copy(buffer, offset) }
  function writeUInt8 (i) { offset = buffer.writeUInt8(i, offset) }
  function writeUInt32 (i) { offset = buffer.writeUInt32LE(i, offset) }
  function writeInt32 (i) { offset = buffer.writeInt32LE(i, offset) }
  function writeUInt64 (i) { offset = bufferutils.writeUInt64LE(buffer, i, offset) }
  function writeVarInt (i) { offset += bufferutils.writeVarInt(buffer, i, offset) }
  function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) }
  function writeVector (vector) { writeVarInt(vector.length); vector.forEach(writeVarSlice) }

  writeInt32(this.version)

  var hasWitnesses = __allowWitness && this.__hasWitnesses()

  if (hasWitnesses) {
    writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER)
    writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG)
  }

  writeVarInt(this.ins.length)

  this.ins.forEach(function (txIn) {
    writeSlice(txIn.hash)
    writeUInt32(txIn.index)
    writeVarSlice(txIn.script)
    writeUInt32(txIn.sequence)
  })

  writeVarInt(this.outs.length)
  this.outs.forEach(function (txOut) {
    if (!txOut.valueBuffer) {
      writeUInt64(txOut.value)
    } else {
      writeSlice(txOut.valueBuffer)
    }

    writeVarSlice(txOut.script)
  })

  if (hasWitnesses) {
    this.ins.forEach(function (input) {
      writeVector(input.witness)
    })
  }

  writeUInt32(this.locktime)

  // avoid slicing unless necessary
  if (initialOffset !== undefined) return buffer.slice(initialOffset, offset)
  return buffer
}

Transaction.prototype.toHex = function () {
  return this.toBuffer().toString('hex')
}

Transaction.prototype.setInputScript = function (index, scriptSig) {
  typeforce(types.tuple(types.Number, types.Buffer), arguments)

  this.ins[index].script = scriptSig
}

Transaction.prototype.setWitness = function (index, witness) {
  typeforce(types.tuple(types.Number, [types.Buffer]), arguments)

  this.ins[index].witness = witness
}

module.exports = Transaction
<|endoftext|>