<commit_before>'use strict'

const ProgressBar = require('progress')
const table = require('table')
const Chalk = require('chalk')
const testColorSupport = require('color-support')
const prettyBytes = require('pretty-bytes')
const xtend = require('xtend')
const format = require('./format')
const percentiles = require('hdr-histogram-percentiles-obj').percentiles
const defaults = {
  // use stderr as its progressBar's default
  outputStream: process.stderr,
  renderProgressBar: true,
  renderResultsTable: true,
  renderLatencyTable: false
}

function track (instance, opts) {
  if (!instance) {
    throw new Error('instance required for tracking')
  }

  opts = xtend(defaults, opts)

  const chalk = new Chalk.constructor({ enabled: testColorSupport({ stream: opts.outputStream }) })
  // this default needs to be set after chalk is setup, because chalk is now local to this func
  opts.progressBarString = opts.progressBarString || `${chalk.green('running')} [:bar] :percent`

  const iOpts = instance.opts
  let durationProgressBar
  let amountProgressBar

  instance.on('start', () => {
    if (opts.renderProgressBar) {
      let msg = `${iOpts.connections} connections`

      if (iOpts.pipelining > 1) {
        msg += ` with ${iOpts.pipelining} pipelining factor`
      }

      if (!iOpts.amount) {
        logToStream(`Running ${iOpts.duration}s test @ ${iOpts.url}\n${msg}\n`)

        durationProgressBar = trackDuration(instance, opts, iOpts)
      } else {
        logToStream(`Running ${iOpts.amount} requests test @ ${iOpts.url}\n${msg}\n`)

        amountProgressBar = trackAmount(instance, opts, iOpts)
      }
    }
  })

  // add listeners for progress bar to instance here so they aren't
  // added on restarting, causing listener leaks
  if (opts.renderProgressBar && opts.outputStream.isTTY) {
    if (!iOpts.amount) { // duration progress bar
      instance.on('tick', () => { durationProgressBar.tick() })
      instance.on('done', () => { durationProgressBar.tick(iOpts.duration - 1) })
      process.once('SIGINT', () => { durationProgressBar.tick(iOpts.duration - 1) })
    } else { // amount progress bar
      instance.on('response', () => { amountProgressBar.tick() })
      instance.on('reqError', () => { amountProgressBar.tick() })
      instance.on('done', () => { amountProgressBar.tick(iOpts.amount - 1) })
      process.once('SIGINT', () => { amountProgressBar.tick(iOpts.amount - 1) })
    }
  }

  instance.on('done', (result) => {
    // the code below this `if` just renders the results table...
    // if the user doesn't want to render the table, we can just return early
    if (!opts.renderResultsTable) return

    const out = table.default([
      asColor(chalk.cyan, ['Stat', 'Avg', 'Stdev', 'Max']),
      asRow(chalk.bold('Latency (ms)'), result.latency),
      asRow(chalk.bold('Req/Sec'), result.requests),
      asRow(chalk.bold('Bytes/Sec'), asBytes(result.throughput))
    ], {
      border: table.getBorderCharacters('void'),
      columnDefault: {
        paddingLeft: 0,
        paddingRight: 1
      },
      drawHorizontalLine: () => false
    })

    logToStream(out)

    if (opts.renderLatencyTable) {
      const latency = table.default([
        asColor(chalk.cyan, ['Percentile', 'Latency (ms)'])
      ].concat(percentiles.map((perc) => {
        const key = ('p' + perc).replace('.', '')
        return [
          chalk.bold('' + perc),
          result.latency[key]
        ]
      })), {
        border: table.getBorderCharacters('void'),
        columnDefault: {
          paddingLeft: 0,
          paddingRight: 6
        },
        drawHorizontalLine: () => false
      })

      logToStream(latency)
    }

    if (result.non2xx) {
      logToStream(`${result['2xx']} 2xx responses, ${result.non2xx} non 2xx responses`)
    }
    logToStream(`${format(result.requests.total)} requests in ${result.duration}s, ${prettyBytes(result.throughput.total)} read`)
    if (result.errors) {
      logToStream(`${format(result.errors)} errors (${format(result.timeouts)} timeouts)`)
    }
  })

  function logToStream (msg) {
    opts.outputStream.write(msg + '\n')
  }
}

function trackDuration (instance, opts, iOpts) {
  // if switch needed needed to avoid
  // https://github.com/mcollina/autocannon/issues/60
  if (!opts.outputStream.isTTY) return

  const progressBar = new ProgressBar(opts.progressBarString, {
    width: 20,
    incomplete: ' ',
    total: iOpts.duration,
    clear: true,
    stream: opts.outputStream
  })

  progressBar.tick(0)
  return progressBar
}

function trackAmount (instance, opts, iOpts) {
  // if switch needed needed to avoid
  // https://github.com/mcollina/autocannon/issues/60
  if (!opts.outputStream.isTTY) return

  const progressBar = new ProgressBar(opts.progressBarString, {
    width: 20,
    incomplete: ' ',
    total: iOpts.amount,
    clear: true,
    stream: opts.outputStream
  })

  progressBar.tick(0)
  return progressBar
}

function asRow (name, stat) {
  return [
    name,
    stat.average,
    stat.stddev,
    stat.max
  ]
}

function asColor (colorise, row) {
  return row.map((entry) => colorise(entry))
}

function asBytes (stat) {
  const result = Object.create(stat)
  result.average = prettyBytes(stat.average)
  result.stddev = prettyBytes(stat.stddev)
  result.max = prettyBytes(stat.max)
  result.min = prettyBytes(stat.min)
  return result
}

module.exports = track
<commit_msg>style nit, curried repeated functionality<commit_after>'use strict'

const ProgressBar = require('progress')
const table = require('table')
const Chalk = require('chalk')
const testColorSupport = require('color-support')
const prettyBytes = require('pretty-bytes')
const xtend = require('xtend')
const format = require('./format')
const percentiles = require('hdr-histogram-percentiles-obj').percentiles
const defaults = {
  // use stderr as its progressBar's default
  outputStream: process.stderr,
  renderProgressBar: true,
  renderResultsTable: true,
  renderLatencyTable: false
}

function track (instance, opts) {
  if (!instance) {
    throw new Error('instance required for tracking')
  }

  opts = xtend(defaults, opts)

  const chalk = new Chalk.constructor({ enabled: testColorSupport({ stream: opts.outputStream }) })
  // this default needs to be set after chalk is setup, because chalk is now local to this func
  opts.progressBarString = opts.progressBarString || `${chalk.green('running')} [:bar] :percent`

  const iOpts = instance.opts
  let durationProgressBar
  let amountProgressBar

  instance.on('start', () => {
    if (opts.renderProgressBar) {
      let msg = `${iOpts.connections} connections`

      if (iOpts.pipelining > 1) {
        msg += ` with ${iOpts.pipelining} pipelining factor`
      }

      if (!iOpts.amount) {
        logToStream(`Running ${iOpts.duration}s test @ ${iOpts.url}\n${msg}\n`)

        durationProgressBar = trackDuration(instance, opts, iOpts)
      } else {
        logToStream(`Running ${iOpts.amount} requests test @ ${iOpts.url}\n${msg}\n`)

        amountProgressBar = trackAmount(instance, opts, iOpts)
      }
    }
  })

  // add listeners for progress bar to instance here so they aren't
  // added on restarting, causing listener leaks
  if (opts.renderProgressBar && opts.outputStream.isTTY) {
    if (!iOpts.amount) { // duration progress bar
      instance.on('tick', curriedTick(durationProgressBar))
      instance.on('done', curriedTick(durationProgressBar, iOpts.duration - 1))
      process.once('SIGINT', curriedTick(durationProgressBar, iOpts.duration - 1))
    } else { // amount progress bar
      instance.on('response', curriedTick(amountProgressBar))
      instance.on('reqError', curriedTick(amountProgressBar))
      instance.on('done', curriedTick(amountProgressBar, iOpts.amount - 1))
      process.once('SIGINT', curriedTick(amountProgressBar, iOpts.amount - 1))
    }
  }

  instance.on('done', (result) => {
    // the code below this `if` just renders the results table...
    // if the user doesn't want to render the table, we can just return early
    if (!opts.renderResultsTable) return

    const out = table.default([
      asColor(chalk.cyan, ['Stat', 'Avg', 'Stdev', 'Max']),
      asRow(chalk.bold('Latency (ms)'), result.latency),
      asRow(chalk.bold('Req/Sec'), result.requests),
      asRow(chalk.bold('Bytes/Sec'), asBytes(result.throughput))
    ], {
      border: table.getBorderCharacters('void'),
      columnDefault: {
        paddingLeft: 0,
        paddingRight: 1
      },
      drawHorizontalLine: () => false
    })

    logToStream(out)

    if (opts.renderLatencyTable) {
      const latency = table.default([
        asColor(chalk.cyan, ['Percentile', 'Latency (ms)'])
      ].concat(percentiles.map((perc) => {
        const key = ('p' + perc).replace('.', '')
        return [
          chalk.bold('' + perc),
          result.latency[key]
        ]
      })), {
        border: table.getBorderCharacters('void'),
        columnDefault: {
          paddingLeft: 0,
          paddingRight: 6
        },
        drawHorizontalLine: () => false
      })

      logToStream(latency)
    }

    if (result.non2xx) {
      logToStream(`${result['2xx']} 2xx responses, ${result.non2xx} non 2xx responses`)
    }
    logToStream(`${format(result.requests.total)} requests in ${result.duration}s, ${prettyBytes(result.throughput.total)} read`)
    if (result.errors) {
      logToStream(`${format(result.errors)} errors (${format(result.timeouts)} timeouts)`)
    }
  })

  function logToStream (msg) {
    opts.outputStream.write(msg + '\n')
  }
}

function trackDuration (instance, opts, iOpts) {
  // if switch needed needed to avoid
  // https://github.com/mcollina/autocannon/issues/60
  if (!opts.outputStream.isTTY) return

  const progressBar = new ProgressBar(opts.progressBarString, {
    width: 20,
    incomplete: ' ',
    total: iOpts.duration,
    clear: true,
    stream: opts.outputStream
  })

  progressBar.tick(0)
  return progressBar
}

function trackAmount (instance, opts, iOpts) {
  // if switch needed needed to avoid
  // https://github.com/mcollina/autocannon/issues/60
  if (!opts.outputStream.isTTY) return

  const progressBar = new ProgressBar(opts.progressBarString, {
    width: 20,
    incomplete: ' ',
    total: iOpts.amount,
    clear: true,
    stream: opts.outputStream
  })

  progressBar.tick(0)
  return progressBar
}

function asRow (name, stat) {
  return [
    name,
    stat.average,
    stat.stddev,
    stat.max
  ]
}

function asColor (colorise, row) {
  return row.map((entry) => colorise(entry))
}

function asBytes (stat) {
  const result = Object.create(stat)
  result.average = prettyBytes(stat.average)
  result.stddev = prettyBytes(stat.stddev)
  result.max = prettyBytes(stat.max)
  result.min = prettyBytes(stat.min)
  return result
}

function curriedTick (progressBar, amount) {
  return () => { progressBar.tick(amount) }
}

module.exports = track
<|endoftext|>


<commit_before>/**
* Module that contains the linked list data structure implementations.
* Depends on core.js, datastructures.js, anim.js, utils.js
*/
/*global JSAV:true */
(function($) {
  "use strict";
  if (typeof JSAV === "undefined") { return; }

  var Edge = JSAV._types.ds.Edge;

  var List = function(jsav, options) {
    this.jsav = jsav;
    this.options = $.extend({visible: true, nodegap: 40, autoresize: true}, options);
    var el = this.options.element || $("
");
    el.addClass("jsavlist");
    for (var key in this.options) {
      var val = this.options[key];
      if (this.options.hasOwnProperty(key) && typeof(val) === "string" || typeof(val) === "number" || typeof(val) === "boolean") {
        el.attr("data-" + key, val);
      }
    }
    if (!this.options.element) {
      $(jsav.canvas).append(el);
    }
    this.element = el;
    this.element.attr({"id": this.id()});
    if (this.options.autoresize) {
      el.addClass("jsavautoresize");
    }
    JSAV.utils._helpers.handlePosition(this);
    JSAV.utils._helpers.handleVisibility(this, this.options);
  };
  JSAV.utils.extend(List, JSAV._types.ds.JSAVDataStructure);
  var listproto = List.prototype;
  listproto.first = function(newFirst, options) {
    if (typeof newFirst === "undefined") {
      return this._first;
    } else {
      return this.addFirst(newFirst, options);
    }
  };
  listproto._setfirst = JSAV.anim(function(newFirst) {
    var oldFirst = this._first;
    this._first = newFirst;
    return [oldFirst];
  });
  listproto.add = function(index, newValue, options) {
    if (index < 0 || index > this.size()) { return this; }
    if (index === 0) {
      return this.addFirst(newValue, options);
    }
    var node = this.get(index - 1),
      newNode;
    if (newValue instanceof ListNode) {
      newNode = newValue;
    } else {
      newNode = this.newNode(newValue);
    }
    if (node) { // there is node for the index
      newNode.next(node.next(), options);
      node.next(newNode, options);
    }
    return this;
  };
  listproto.addFirst = function(newValue, options) {
    if (newValue instanceof ListNode) {
      newValue.next(this._first, options);
      this._setfirst(newValue, options);
    } else {
      this._setfirst(this.newNode(newValue, $.extend({}, options, {first: true, next: this._first})), options);
    }
    return this;
  };
  /** returns the last item in the list or if newLast is given, adds it to the end */
  listproto.last = function(newLast, options) {
    if (typeof newLast === "undefined") {
      var curNode = this.first();
      while (curNode && curNode.next()) {
        curNode = curNode.next();
      }
      return curNode;
    } else {
      this.addLast(newLast, options);
    }
  };
  /** adds the given value/node as the last item in the list */
  listproto.addLast = function(newValue, options) {
    var last = this.last(),
        newNode;
    if (typeof last === "undefined") { // if no last, add to first
      this.first(newValue);
      return this;
    }
    if (newValue instanceof ListNode) {
      newNode = newValue;
    } else {
      newNode = this.newNode(newValue);
    }
    last.next(newNode, options);
    return this;
  };
  /** Returns the item at index, first node is at index 0 */
  listproto.get = function(index) {
    if (typeof(index) !== "number" || index < 0) { return; }
    var curNode = this.first(),
        pos = 0;
    while (curNode.next() && pos < index) {
      curNode = curNode.next();
      pos++;
    }
    if (pos === index) {
      return curNode;
    } else {
      return undefined;
    }
  };
  listproto.newNode = function(value, options) {
    return new ListNode(this, value, $.extend({first: false}, this.options, options));
  };
  listproto.remove = function(index, options) {
    // TODO: remove bounds checks -> use removefirst/last
    if (index === 0) {
      return this.removeFirst(options);
    } else if (index === this.size() - 1) {
      return this.removeLast(options);
    }
    var prev = this.get(index - 1),
        next = this.get(index + 1),
        oldNode = prev.next();
    prev.next(next, options);
    return oldNode;
  };
  listproto.removeFirst = function(options) {
    var oldFirst = this.first();
    this._setfirst(oldFirst.next(), options);
    oldFirst.hide();
    return oldFirst;
  };
  listproto.removeLast = function(options) {
    var newLast = this.get(this.size() - 2),
      oldLast = this.last();
    newLast.next(null, options);
    return oldLast;
  };
  listproto.layout = function(options) {
    var layoutAlg = $.extend({}, this.options, options).layout || "_default";
    return this.jsav.ds.layout.list[layoutAlg](this, options);
  };
  listproto.state = function(newState) {
    // TODO: implement list.state
  };
  listproto.clear = function() {
    this.element.remove();
  };
  listproto.size = function() {
    var curNode = this.first(),
      size = 0;
    while (curNode) {
      size++;
      curNode = curNode.next();
    }
    return size;
  };
  listproto.css = JSAV.utils._helpers.css;
  listproto._setcss = JSAV.anim(JSAV.utils._helpers._setcss);

  // add the event handler registration functions to the list prototype
  JSAV.utils._events._addEventSupport(listproto);

  var ListNode = function(container, value, options) {
    this.jsav = container.jsav;
    this.container = container;
    this._next = options.next;
    this._value = value;
    this.options = $.extend(true, {visible: true}, options);
    var el = $("
" + this._valstring(value) + "
"),
      valtype = typeof(value);
    if (valtype === "object") { valtype = "string"; }
    this.element = el;
    el.addClass("jsavnode jsavlistnode")
        .attr({"data-value": value, "id": this.id(), "data-value-type": valtype })
        .data("node", this);
    if ("first" in options && options.first) {
      this.container.element.prepend(el);
    } else {
      this.container.element.append(el);
    }
    if (this._next) {
      this._edgetonext = new Edge(this.jsav, this, this._next, {"arrow-end": "classic-wide-long"});
      if (this.options.edgeLabel) {
        this._edgetonext.label(this.options.edgeLabel);
      }
    }
    JSAV.utils._helpers.handleVisibility(this, this.options);
  };

  JSAV.utils.extend(ListNode, JSAV._types.ds.Node);
  var listnodeproto = ListNode.prototype;

  listnodeproto.next = function(newNext, options) {
    if (typeof newNext === "undefined") {
      return this._next;
    } else {
      return this._setnext(newNext, options);
    }
  };
  listnodeproto._setnext = JSAV.anim(function(newNext, options) {
    var oldNext = this._next;
    this._next = newNext;
    if (newNext && this._edgetonext) {
      this._edgetonext.end(newNext);
    } else if (newNext){
      this._edgetonext = new Edge(this.jsav, this, this._next, {"arrow-end": "classic-wide-long"});
    }
    if (options && options.edgeLabel) {
      this._edgetonext.label(options.edgeLabel);
    }
    return [oldNext];
  });
  listnodeproto.edgeToNext = function() {
    return this._edgetonext;
  };
  listnodeproto.state = function(newState) {
    // TODO: implement state
  };

  // expose the list types
  var dstypes = JSAV._types.ds;
  dstypes.List = List;
  dstypes.ListNode = ListNode;

  function centerList(list, width, options) {
    // center the list inside its parent container
    if (list.options.hasOwnProperty("center") && !list.options.center) {
      // if options center is set to falsy value, return
      return list.position().left;
    }
    // width of list expected to be last items position + its width
    var containerWidth = $(list.jsav.canvas).width();
    return (containerWidth - width)/2;
  }

  var horizontalNodePosUpdate = function(node, prevNode, prevPos, opts) {
    // function for calculating node positions in horizontal list
    var nodePos = node.element.position(),
        newPos = { left: nodePos.left, top: nodePos.top }; // by default, don't move it
    if (opts.updateLeft) { newPos.left = prevNode?(prevPos.left +
                            prevNode.element.outerWidth() + opts.nodegap):0; }
    if (opts.updateTop) { newPos.top = 0; }
    var edge = prevNode?prevNode._edgetonext:undefined;
    if (edge && opts.updateEdges) {
      var start = [0, prevPos.left + prevNode.element.outerWidth() - 5,
                  prevPos.top + Math.round(prevNode.element.outerHeight()/2)],
          end = [1, newPos.left - 3,
                  newPos.top + Math.round(node.element.outerHeight()/2)];
      return [newPos, [start, end]];
    }
    return [newPos];
  };
  var verticalNodePosUpdate = function(node, prevNode, prevPos, opts) {
    // function for calculating node positions in vertical list
    var nodePos = node.element.position(),
        newPos = { left: nodePos.left, top: nodePos.top };
    if (opts.updateLeft) { newPos.left = 0; }
    if (opts.updateTop) { newPos.top = prevNode?(prevPos.top +
                          prevNode.element.outerHeight() + opts.nodegap):0; }
    var edge = prevNode?prevNode._edgetonext:undefined;
    if (edge && opts.updateEdges) {
      var start = [0, prevPos.left + Math.round(prevNode.element.width()/2),
                  prevPos.top + Math.round(prevNode.element.height()) + 2],
          end = [1, newPos.left + Math.round(prevNode.element.width()/2),
                  Math.round(newPos.top - 4)];
      return [newPos, [start, end]];
    }
    return [newPos];
  };
  var listLayout = function(list, options, updateFunc) {
    // a general list layout that goes through the nodes and calls given updateFunc
    // to calculate the new node positions
    var curNode = list.first(),
        prevNode,
        opts = $.extend({updateLeft: true, updateTop: true, updateEdges: true}, list.options, options),
        prevPos = {},
        minLeft = Number.MAX_VALUE,
        minTop = Number.MAX_VALUE,
        maxLeft = Number.MIN_VALUE,
        maxTop = Number.MIN_VALUE,
        width,
        height,
        left,
        posData = [],
        nodePos;
    // two phase layout: first go through all the nodes calculate positions
    while (curNode) {
      nodePos = updateFunc(curNode, prevNode, prevPos, opts);
      prevPos = nodePos[0];
      // keep track of max and min coordinates to calculate the size of the container
      minLeft = (typeof prevPos.left !== "undefined")?Math.min(prevPos.left, minLeft):minLeft;
      minTop  = (typeof prevPos.top  !== "undefined")?Math.min(prevPos.top, minTop):minTop;
      maxLeft = (typeof prevPos.left !== "undefined")?Math.max(prevPos.left + curNode.element.outerWidth(), maxLeft):maxLeft;
      maxTop  = (typeof prevPos.top  !== "undefined")?Math.max(prevPos.top + curNode.element.outerHeight(), maxTop):maxTop;
      posData.unshift({node: curNode, nodePos: prevPos});
      // if we also have edge position data, store that
      if (nodePos.length > 1) {
        posData[0].edgePos = nodePos[1];
        posData[0].edge = prevNode.edgeToNext();
      }
      // go to next node and continue with that
      prevNode = curNode;
      curNode = curNode.next();
    }
    if (list.size()) {
      width = maxLeft - minLeft;
      height = maxTop - minTop;
    } else {
      var tmpNode = list.newNode("");
      width = tmpNode.element.outerWidth();
      height = tmpNode.element.outerHeight();
      tmpNode.clear();
    }
    left = centerList(list, width, opts);
    if (!opts.boundsOnly) {
      // ..update list size and position..
      list.css({width: width, height: height, left: left});
      // .. and finally update the node and edge positions
      // doing the size first makes the animation look smoother by reducing some flicker
      for (var i = posData.length - 1; i >= 0; i--) {
        var posItem = posData[i];
        posItem.node.css(posItem.nodePos);
        if (posItem.edge) {
          posItem.edge.g.movePoints(posItem.edgePos, opts);
        }
      }
    }
    return { width: width, height: height, left: left, top: list.element.position().top };
  };
  var verticalList = function(list, options) {
    list.element.addClass("jsavverticallist");
    // use the general list layout with verticalNodePosUpdate as the calculator
    return listLayout(list, options, verticalNodePosUpdate);
  };
  var horizontalList = function(list, options) {
    list.element.addClass("jsavhorizontallist");
    // use the general list layout with horizontalNodePosUpdate as the calculator
    return listLayout(list, options, horizontalNodePosUpdate);
  };

  JSAV.ext.ds.layout.list = {
    "_default": horizontalList,
    "horizontal": horizontalList,
    "vertical": verticalList
  };

  JSAV.ext.ds.list = function(options) {
    return new List(this, options);
  };
}(jQuery));<commit_msg>List nodes are now hidden when removed unless hide: false option is passed (fixes #105)<commit_after>/**
* Module that contains the linked list data structure implementations.
* Depends on core.js, datastructures.js, anim.js, utils.js
*/
/*global JSAV:true */
(function($) {
  "use strict";
  if (typeof JSAV === "undefined") { return; }

  var Edge = JSAV._types.ds.Edge;

  var List = function(jsav, options) {
    this.jsav = jsav;
    this.options = $.extend({visible: true, nodegap: 40, autoresize: true}, options);
    var el = this.options.element || $("
");
    el.addClass("jsavlist");
    for (var key in this.options) {
      var val = this.options[key];
      if (this.options.hasOwnProperty(key) && typeof(val) === "string" || typeof(val) === "number" || typeof(val) === "boolean") {
        el.attr("data-" + key, val);
      }
    }
    if (!this.options.element) {
      $(jsav.canvas).append(el);
    }
    this.element = el;
    this.element.attr({"id": this.id()});
    if (this.options.autoresize) {
      el.addClass("jsavautoresize");
    }
    JSAV.utils._helpers.handlePosition(this);
    JSAV.utils._helpers.handleVisibility(this, this.options);
  };
  JSAV.utils.extend(List, JSAV._types.ds.JSAVDataStructure);
  var listproto = List.prototype;
  listproto.first = function(newFirst, options) {
    if (typeof newFirst === "undefined") {
      return this._first;
    } else {
      return this.addFirst(newFirst, options);
    }
  };
  listproto._setfirst = JSAV.anim(function(newFirst) {
    var oldFirst = this._first;
    this._first = newFirst;
    return [oldFirst];
  });
  listproto.add = function(index, newValue, options) {
    if (index < 0 || index > this.size()) { return this; }
    if (index === 0) {
      return this.addFirst(newValue, options);
    }
    var node = this.get(index - 1),
      newNode;
    if (newValue instanceof ListNode) {
      newNode = newValue;
    } else {
      newNode = this.newNode(newValue);
    }
    if (node) { // there is node for the index
      newNode.next(node.next(), options);
      node.next(newNode, options);
    }
    return this;
  };
  listproto.addFirst = function(newValue, options) {
    if (newValue instanceof ListNode) {
      newValue.next(this._first, options);
      this._setfirst(newValue, options);
    } else {
      this._setfirst(this.newNode(newValue, $.extend({}, options, {first: true, next: this._first})), options);
    }
    return this;
  };
  /** returns the last item in the list or if newLast is given, adds it to the end */
  listproto.last = function(newLast, options) {
    if (typeof newLast === "undefined") {
      var curNode = this.first();
      while (curNode && curNode.next()) {
        curNode = curNode.next();
      }
      return curNode;
    } else {
      this.addLast(newLast, options);
    }
  };
  /** adds the given value/node as the last item in the list */
  listproto.addLast = function(newValue, options) {
    var last = this.last(),
        newNode;
    if (typeof last === "undefined") { // if no last, add to first
      this.first(newValue);
      return this;
    }
    if (newValue instanceof ListNode) {
      newNode = newValue;
    } else {
      newNode = this.newNode(newValue);
    }
    last.next(newNode, options);
    return this;
  };
  /** Returns the item at index, first node is at index 0 */
  listproto.get = function(index) {
    if (typeof(index) !== "number" || index < 0) { return; }
    var curNode = this.first(),
        pos = 0;
    while (curNode.next() && pos < index) {
      curNode = curNode.next();
      pos++;
    }
    if (pos === index) {
      return curNode;
    } else {
      return undefined;
    }
  };
  listproto.newNode = function(value, options) {
    return new ListNode(this, value, $.extend({first: false}, this.options, options));
  };
  listproto.remove = function(index, options) {
    var opts = $.extend({hide: true}, options);
    if (index === 0) {
      return this.removeFirst(options);
    } else if (index === this.size() - 1) {
      return this.removeLast(options);
    }
    var prev = this.get(index - 1),
        next = this.get(index + 1),
        oldNode = prev.next();
    prev.next(next, options);
    if (opts.hide) {
      oldNode.hide();
      oldNode.edgeToNext().hide();
    }
    return oldNode;
  };
  listproto.removeFirst = function(options) {
    if (this.size() <= 0) { return; }
    var opts = $.extend({hide: true}, options),
        oldFirst = this.first();
    this._setfirst(oldFirst.next(), options);
    if (opts.hide) {
      oldFirst.hide();
      oldFirst.edgeToNext().hide();
    }
    return oldFirst;
  };
  listproto.removeLast = function(options) {
    if (this.size() <= 1) { return this.removeFirst(); }
    var opts = $.extend({hide: true}, options),
        newLast = this.get(this.size() - 2),
        oldLast = this.last();
    newLast.next(null, options);
    if (opts.hide) {
      oldLast.hide();
      newLast.edgeToNext().hide();
    }
    return oldLast;
  };
  listproto.layout = function(options) {
    var layoutAlg = $.extend({}, this.options, options).layout || "_default";
    return this.jsav.ds.layout.list[layoutAlg](this, options);
  };
  listproto.state = function(newState) {
    // TODO: implement list.state
  };
  listproto.clear = function() {
    this.element.remove();
  };
  listproto.size = function() {
    var curNode = this.first(),
      size = 0;
    while (curNode) {
      size++;
      curNode = curNode.next();
    }
    return size;
  };
  listproto.css = JSAV.utils._helpers.css;
  listproto._setcss = JSAV.anim(JSAV.utils._helpers._setcss);

  // add the event handler registration functions to the list prototype
  JSAV.utils._events._addEventSupport(listproto);

  var ListNode = function(container, value, options) {
    this.jsav = container.jsav;
    this.container = container;
    this._next = options.next;
    this._value = value;
    this.options = $.extend(true, {visible: true}, options);
    var el = $("
" + this._valstring(value) + "
"),
      valtype = typeof(value);
    if (valtype === "object") { valtype = "string"; }
    this.element = el;
    el.addClass("jsavnode jsavlistnode")
        .attr({"data-value": value, "id": this.id(), "data-value-type": valtype })
        .data("node", this);
    if ("first" in options && options.first) {
      this.container.element.prepend(el);
    } else {
      this.container.element.append(el);
    }
    if (this._next) {
      this._edgetonext = new Edge(this.jsav, this, this._next, {"arrow-end": "classic-wide-long"});
      if (this.options.edgeLabel) {
        this._edgetonext.label(this.options.edgeLabel);
      }
    }
    JSAV.utils._helpers.handleVisibility(this, this.options);
  };

  JSAV.utils.extend(ListNode, JSAV._types.ds.Node);
  var listnodeproto = ListNode.prototype;

  listnodeproto.next = function(newNext, options) {
    if (typeof newNext === "undefined") {
      return this._next;
    } else {
      return this._setnext(newNext, options);
    }
  };
  listnodeproto._setnext = JSAV.anim(function(newNext, options) {
    var oldNext = this._next;
    this._next = newNext;
    if (newNext && this._edgetonext) {
      this._edgetonext.end(newNext);
    } else if (newNext){
      this._edgetonext = new Edge(this.jsav, this, this._next, {"arrow-end": "classic-wide-long"});
    }
    if (options && options.edgeLabel) {
      this._edgetonext.label(options.edgeLabel);
    }
    return [oldNext];
  });
  listnodeproto.edgeToNext = function() {
    return this._edgetonext;
  };
  listnodeproto.state = function(newState) {
    // TODO: implement state
  };

  // expose the list types
  var dstypes = JSAV._types.ds;
  dstypes.List = List;
  dstypes.ListNode = ListNode;

  function centerList(list, width, options) {
    // center the list inside its parent container
    if (list.options.hasOwnProperty("center") && !list.options.center) {
      // if options center is set to falsy value, return
      return list.position().left;
    }
    // width of list expected to be last items position + its width
    var containerWidth = $(list.jsav.canvas).width();
    return (containerWidth - width)/2;
  }

  var horizontalNodePosUpdate = function(node, prevNode, prevPos, opts) {
    // function for calculating node positions in horizontal list
    var nodePos = node.element.position(),
        newPos = { left: nodePos.left, top: nodePos.top }; // by default, don't move it
    if (opts.updateLeft) { newPos.left = prevNode?(prevPos.left +
                            prevNode.element.outerWidth() + opts.nodegap):0; }
    if (opts.updateTop) { newPos.top = 0; }
    var edge = prevNode?prevNode._edgetonext:undefined;
    if (edge && opts.updateEdges) {
      var start = [0, prevPos.left + prevNode.element.outerWidth() - 5,
                  prevPos.top + Math.round(prevNode.element.outerHeight()/2)],
          end = [1, newPos.left - 3,
                  newPos.top + Math.round(node.element.outerHeight()/2)];
      return [newPos, [start, end]];
    }
    return [newPos];
  };
  var verticalNodePosUpdate = function(node, prevNode, prevPos, opts) {
    // function for calculating node positions in vertical list
    var nodePos = node.element.position(),
        newPos = { left: nodePos.left, top: nodePos.top };
    if (opts.updateLeft) { newPos.left = 0; }
    if (opts.updateTop) { newPos.top = prevNode?(prevPos.top +
                          prevNode.element.outerHeight() + opts.nodegap):0; }
    var edge = prevNode?prevNode._edgetonext:undefined;
    if (edge && opts.updateEdges) {
      var start = [0, prevPos.left + Math.round(prevNode.element.width()/2),
                  prevPos.top + Math.round(prevNode.element.height()) + 2],
          end = [1, newPos.left + Math.round(prevNode.element.width()/2),
                  Math.round(newPos.top - 4)];
      return [newPos, [start, end]];
    }
    return [newPos];
  };
  var listLayout = function(list, options, updateFunc) {
    // a general list layout that goes through the nodes and calls given updateFunc
    // to calculate the new node positions
    var curNode = list.first(),
        prevNode,
        opts = $.extend({updateLeft: true, updateTop: true, updateEdges: true}, list.options, options),
        prevPos = {},
        minLeft = Number.MAX_VALUE,
        minTop = Number.MAX_VALUE,
        maxLeft = Number.MIN_VALUE,
        maxTop = Number.MIN_VALUE,
        width,
        height,
        left,
        posData = [],
        nodePos;
    // two phase layout: first go through all the nodes calculate positions
    while (curNode) {
      nodePos = updateFunc(curNode, prevNode, prevPos, opts);
      prevPos = nodePos[0];
      // keep track of max and min coordinates to calculate the size of the container
      minLeft = (typeof prevPos.left !== "undefined")?Math.min(prevPos.left, minLeft):minLeft;
      minTop  = (typeof prevPos.top  !== "undefined")?Math.min(prevPos.top, minTop):minTop;
      maxLeft = (typeof prevPos.left !== "undefined")?Math.max(prevPos.left + curNode.element.outerWidth(), maxLeft):maxLeft;
      maxTop  = (typeof prevPos.top  !== "undefined")?Math.max(prevPos.top + curNode.element.outerHeight(), maxTop):maxTop;
      posData.unshift({node: curNode, nodePos: prevPos});
      // if we also have edge position data, store that
      if (nodePos.length > 1) {
        posData[0].edgePos = nodePos[1];
        posData[0].edge = prevNode.edgeToNext();
      }
      // go to next node and continue with that
      prevNode = curNode;
      curNode = curNode.next();
    }
    if (list.size()) {
      width = maxLeft - minLeft;
      height = maxTop - minTop;
    } else {
      var tmpNode = list.newNode("");
      width = tmpNode.element.outerWidth();
      height = tmpNode.element.outerHeight();
      tmpNode.clear();
    }
    left = centerList(list, width, opts);
    if (!opts.boundsOnly) {
      // ..update list size and position..
      list.css({width: width, height: height, left: left});
      // .. and finally update the node and edge positions
      // doing the size first makes the animation look smoother by reducing some flicker
      for (var i = posData.length - 1; i >= 0; i--) {
        var posItem = posData[i];
        posItem.node.css(posItem.nodePos);
        if (posItem.edge) {
          posItem.edge.g.movePoints(posItem.edgePos, opts);
        }
      }
    }
    return { width: width, height: height, left: left, top: list.element.position().top };
  };
  var verticalList = function(list, options) {
    list.element.addClass("jsavverticallist");
    // use the general list layout with verticalNodePosUpdate as the calculator
    return listLayout(list, options, verticalNodePosUpdate);
  };
  var horizontalList = function(list, options) {
    list.element.addClass("jsavhorizontallist");
    // use the general list layout with horizontalNodePosUpdate as the calculator
    return listLayout(list, options, horizontalNodePosUpdate);
  };

  JSAV.ext.ds.layout.list = {
    "_default": horizontalList,
    "horizontal": horizontalList,
    "vertical": verticalList
  };

  JSAV.ext.ds.list = function(options) {
    return new List(this, options);
  };
}(jQuery));<|endoftext|>


<commit_before>module.exports = function(sysinfo) {

    var providers = {
        'linux': require('./linux')(sysinfo),
        'mac': require('./mac')(sysinfo),
        'win': require('./win')(sysinfo),
        'other': require('./other')(sysinfo)
    };

    var provider = providers[sysinfo.OS];
    if(provider) {
        return provider;
    } else {
        return providers['other'];
    }
}<commit_msg>improved performance<commit_after>var providers = {
    'linux': require('./linux'),
    'mac': require('./mac'),
    'win': require('./win'),
    'other': require('./other')
};

module.exports = function(sysinfo) {

    var provider = providers[sysinfo.OS];
    if(provider) {
        return provider(sysinfo);
    } else {
        return providers['other'];
    }
}<|endoftext|>


<commit_before>/* eslint prefer-reflect: "off" */

import { select, transition } from 'd3';
import { Observer } from '@scola/d3-model';

export default class List extends Observer {
  constructor() {
    super();

    this._items = new Set();

    this._gesture = null;
    this._rootMedia = null;
    this._bodyMedia = null;
    this._inset = false;

    this._title = null;
    this._comment = null;

    this._root = select('body')
      .append('div')
      .remove()
      .classed('scola list', true)
      .styles({
        'box-sizing': 'content-box',
        'margin-bottom': '2em',
        'overflow': 'hidden'
      });

    this._title = this._root
      .append('div')
      .classed('scola title', true)
      .styles({
        'color': '#AAA',
        'display': 'none',
        'font-size': '0.9em',
        'justify-content': 'space-between',
        'order': 1,
        'padding': '0 1.111em 0.555em',
        'text-transform': 'uppercase'
      });

    this._titleText = this._title
      .append('span')
      .classed('scola text', true)
      .styles({
        'font-size': 'inherit'
      });

    this._titleIcon = this._title
      .append('span')
      .classed('scola icon ion-ios-arrow-down', true)
      .styles({
        'display': 'none',
        'font-size': 'inherit',
        'height': '1em',
        'text-align': 'center',
        'width': '1em'
      });

    this._body = this._root
      .append('div')
      .classed('scola body', true)
      .styles({
        'border-color': '#CCC',
        'border-style': 'solid',
        'border-width': '1px 0',
        'overflow': 'hidden'
      });

    this._comment = this._root
      .append('div')
      .classed('scola comment', true)
      .styles({
        'color': '#AAA',
        'display': 'none',
        'font-size': '0.9em',
        'line-height': '1.5em',
        'padding': '0.75em 1.1em 0'
      });

    this._bindBody();
  }

  destroy() {
    this._unbindBody();
    this._unbindTitle();
    this._deleteInset();

    this._items.forEach((item) => {
      item.destroy();
    });

    this._items.clear();

    this._root.dispatch('destroy');
    this._root.remove();
    this._root = null;
  }

  root() {
    return this._root;
  }

  body() {
    return this._body;
  }

  items() {
    return this._items;
  }

  model(value) {
    this._root
      .style('height', 0);

    this._titleIcon
      .style('display', 'initial');

    this._bindTitle();
    return super.model(value);
  }

  inset(width = '48em') {
    if (width === null) {
      return this._inset;
    }

    if (width === false) {
      this._deleteInset();
    }

    if (this._rootMedia === null) {
      this._insertInset(width);
    }

    return this;
  }

  title(value = null) {
    if (value === null) {
      return this._title;
    }

    if (value === false) {
      return this._hideTitle();
    }

    return this._showTitle(value);
  }

  comment(value = null) {
    if (value === null) {
      return this._comment;
    }

    if (value === false) {
      return this._hideComment();
    }

    return this._showComment(value);
  }

  append(item, action = true) {
    if (action === false) {
      return this._deleteItem(item);
    }

    return this._insertItem(item);
  }

  disabled(value) {
    this._items.forEach((item) => item.disabled(value));
    return this;
  }

  _bindBody() {
    this._gesture = this._body
      .gesture()
      .on('panstart', (e) => e.stopPropagation())
      .on('panright', (e) => e.stopPropagation())
      .on('panleft', (e) => e.stopPropagation())
      .on('panend', (e) => e.stopPropagation())
      .on('swiperight', (e) => e.stopPropagation())
      .on('swipeleft', (e) => e.stopPropagation())
      .on('tap', (e) => e.stopPropagation());
  }

  _unbindBody() {
    if (this._gesture) {
      this._gesture.destroy();
      this._gesture = null;
    }
  }

  _insertInset(width) {
    this._rootMedia = this._root
      .media(`not all and (min-width: ${width})`)
      .call(() => { this._inset = false; })
      .media(`(min-width: ${width})`)
      .call(() => { this._inset = true; })
      .styles({
        'margin-left': '1em',
        'margin-right': '1em'
      })
      .start();

    this._bodyMedia = this._body
      .media(`(min-width: ${width})`)
      .styles({
        'border-radius': '0.5em',
        'border-style': 'none'
      })
      .start();

    return this;
  }

  _deleteInset() {
    if (this._rootMedia) {
      this._rootMedia.destroy();
      this._rootMedia = null;
    }

    if (this._bodyMedia) {
      this._bodyMedia.destroy();
      this._bodyMedia = null;
    }

    return this;
  }

  _showTitle(title) {
    this._title
      .style('display', 'flex');

    this._titleText
      .text(title);

    return this;
  }

  _hideTitle() {
    this._title
      .style('display', 'none');

    return this;
  }

  _showComment(comment) {
    this._comment
      .style('display', 'block')
      .text(comment);

    return this;
  }

  _hideComment() {
    this._comment
      .style('display', 'none');

    return this;
  }

  _insertItem(item) {
    item.first(this._items.size === 0);

    this._items.add(item);
    this._body.append(() => item.root().node());

    return item;
  }

  _deleteItem(item) {
    this._items.delete(item);
    item.root().remove();

    return item;
  }

  _bindTitle() {
    if (this._title) {
      this._title
        .classed('click', true)
        .style('cursor', 'pointer')
        .on('click.scola-list', () => this._click());
    }
  }

  _unbindTitle() {
    if (this._title) {
      this._title
        .classed('click', false)
        .style('cursor', 'initial')
        .on('click.scola-list', null);
    }
  }

  _click() {
    if (this._model) {
      const value = this._model.get(this._name);
      this._model.set(this._name, value === false ? true : false);
    }
  }

  _set(setEvent) {
    if (setEvent.name !== this._name) {
      return;
    }

    const bodyHeight = parseFloat(this._body.style('height')) || 0;
    const commentHeight = parseFloat(this._comment.style('height')) || 0;
    const titleHeight = parseFloat(this._title.style('height')) || 0;

    const duration = setEvent.changed === false ? 0 : 250;

    const timeline = transition()
      .duration(duration)
      .on('end', () => {
        setTimeout(() => {
          if (setEvent.value !== false) {
            this._root.style('height', null);
          } else {
            this._root.style('border-bottom', '1px solid #CCC');
          }
        });
      });

    if (setEvent.value === true) {
      this._root.style('border', 0);
    }

    this._root
      .transition(timeline)
      .style('height', () => {
        return setEvent.value !== false ? (
          titleHeight +
          bodyHeight +
          commentHeight
        ) + 'px' : titleHeight + 'px';
      });

    this._titleIcon
      .transition(timeline)
      .style('transform', () => {
        return setEvent.value !== false ?
          'rotate(0deg)' :
          'rotate(-90deg)';
      });
  }
}
<commit_msg>Set list height directly, add clear method<commit_after>/* eslint prefer-reflect: "off" */

import { select, transition } from 'd3';
import { Observer } from '@scola/d3-model';

export default class List extends Observer {
  constructor() {
    super();

    this._items = new Set();

    this._gesture = null;
    this._rootMedia = null;
    this._bodyMedia = null;
    this._inset = false;

    this._title = null;
    this._comment = null;

    this._root = select('body')
      .append('div')
      .remove()
      .classed('scola list', true)
      .styles({
        'box-sizing': 'content-box',
        'margin-bottom': '2em',
        'overflow': 'hidden'
      });

    this._title = this._root
      .append('div')
      .classed('scola title', true)
      .styles({
        'color': '#AAA',
        'display': 'none',
        'font-size': '0.9em',
        'justify-content': 'space-between',
        'order': 1,
        'padding': '0 1.111em 0.555em',
        'text-transform': 'uppercase'
      });

    this._titleText = this._title
      .append('span')
      .classed('scola text', true)
      .styles({
        'font-size': 'inherit'
      });

    this._titleIcon = this._title
      .append('span')
      .classed('scola icon ion-ios-arrow-down', true)
      .styles({
        'display': 'none',
        'font-size': 'inherit',
        'height': '1em',
        'text-align': 'center',
        'width': '1em'
      });

    this._body = this._root
      .append('div')
      .classed('scola body', true)
      .styles({
        'border-color': '#CCC',
        'border-style': 'solid',
        'border-width': '1px 0',
        'overflow': 'hidden'
      });

    this._comment = this._root
      .append('div')
      .classed('scola comment', true)
      .styles({
        'color': '#AAA',
        'display': 'none',
        'font-size': '0.9em',
        'line-height': '1.5em',
        'padding': '0.75em 1.1em 0'
      });

    this._bindBody();
  }

  destroy() {
    this._unbindBody();
    this._unbindTitle();
    this._deleteInset();

    this._items.forEach((item) => {
      item.destroy();
    });

    this._items.clear();

    this._root.dispatch('destroy');
    this._root.remove();
    this._root = null;
  }

  root() {
    return this._root;
  }

  body() {
    return this._body;
  }

  items() {
    return this._items;
  }

  model(value = null) {
    value = super.model(value);

    if (value === null) {
      return value;
    }

    if (this._model.get(this._name) === false) {
      this._root.styles({
        'height': '1.5em',
        'border-bottom': '1px solid #CCC'
      });
    }

    this._titleIcon
      .style('display', 'initial');

    this._bindTitle();
    return this;
  }

  inset(width = '48em') {
    if (width === null) {
      return this._inset;
    }

    if (width === false) {
      this._deleteInset();
    }

    if (this._rootMedia === null) {
      this._insertInset(width);
    }

    return this;
  }

  title(value = null) {
    if (value === null) {
      return this._title;
    }

    if (value === false) {
      return this._hideTitle();
    }

    return this._showTitle(value);
  }

  comment(value = null) {
    if (value === null) {
      return this._comment;
    }

    if (value === false) {
      return this._hideComment();
    }

    return this._showComment(value);
  }

  append(item, action = true) {
    if (action === false) {
      return this._deleteItem(item);
    }

    return this._insertItem(item);
  }

  clear() {
    this._items.forEach((item) => {
      item.destroy();
    });

    this._items.clear();
    return this;
  }

  disabled(value) {
    this._items.forEach((item) => {
      item.disabled(value);
    });

    return this;
  }

  _bindBody() {
    this._gesture = this._body
      .gesture()
      .on('panstart', (e) => e.stopPropagation())
      .on('panright', (e) => e.stopPropagation())
      .on('panleft', (e) => e.stopPropagation())
      .on('panend', (e) => e.stopPropagation())
      .on('swiperight', (e) => e.stopPropagation())
      .on('swipeleft', (e) => e.stopPropagation())
      .on('tap', (e) => e.stopPropagation());
  }

  _unbindBody() {
    if (this._gesture) {
      this._gesture.destroy();
      this._gesture = null;
    }
  }

  _insertInset(width) {
    this._rootMedia = this._root
      .media(`not all and (min-width: ${width})`)
      .call(() => { this._inset = false; })
      .media(`(min-width: ${width})`)
      .call(() => { this._inset = true; })
      .styles({
        'margin-left': '1em',
        'margin-right': '1em'
      })
      .start();

    this._bodyMedia = this._body
      .media(`(min-width: ${width})`)
      .styles({
        'border-radius': '0.5em',
        'border-style': 'none'
      })
      .start();

    return this;
  }

  _deleteInset() {
    if (this._rootMedia) {
      this._rootMedia.destroy();
      this._rootMedia = null;
    }

    if (this._bodyMedia) {
      this._bodyMedia.destroy();
      this._bodyMedia = null;
    }

    return this;
  }

  _showTitle(title) {
    this._title
      .style('display', 'flex');

    this._titleText
      .text(title);

    return this;
  }

  _hideTitle() {
    this._title
      .style('display', 'none');

    return this;
  }

  _showComment(comment) {
    this._comment
      .style('display', 'block')
      .text(comment);

    return this;
  }

  _hideComment() {
    this._comment
      .style('display', 'none');

    return this;
  }

  _insertItem(item) {
    item.first(this._items.size === 0);

    this._items.add(item);
    this._body.append(() => item.root().node());

    return item;
  }

  _deleteItem(item) {
    this._items.delete(item);
    item.root().remove();

    return item;
  }

  _bindTitle() {
    if (this._title) {
      this._title
        .classed('click', true)
        .style('cursor', 'pointer')
        .on('click.scola-list', () => this._click());
    }
  }

  _unbindTitle() {
    if (this._title) {
      this._title
        .classed('click', false)
        .style('cursor', 'initial')
        .on('click.scola-list', null);
    }
  }

  _click() {
    if (this._model) {
      const value = this._model.get(this._name);
      this._model.set(this._name, value === false ? true : false);
    }
  }

  _set(setEvent) {
    if (setEvent.name !== this._name) {
      return;
    }

    const bodyHeight = parseFloat(this._body.style('height')) || 0;
    const commentHeight = parseFloat(this._comment.style('height')) || 0;
    const titleHeight = parseFloat(this._title.style('height')) || 0;

    const duration = setEvent.changed === false ? 0 : 250;

    const timeline = transition()
      .duration(duration)
      .on('end', () => {
        setTimeout(() => {
          if (setEvent.value !== false) {
            this._root.style('height', null);
          } else {
            this._root.style('border-bottom', '1px solid #CCC');
          }
        });
      });

    if (setEvent.value === true) {
      this._root.style('border', 0);
    }

    this._root
      .transition(timeline)
      .style('height', () => {
        return setEvent.value !== false ? (
          titleHeight +
          bodyHeight +
          commentHeight
        ) + 'px' : titleHeight + 'px';
      });

    this._titleIcon
      .transition(timeline)
      .style('transform', () => {
        return setEvent.value !== false ?
          'rotate(0deg)' :
          'rotate(-90deg)';
      });
  }
}
<|endoftext|>


<commit_before>const parse = (text) => {
    const languages = {};
    text.trim()
        .split(' ')
        .forEach((string) => {
            const [key, value] = string.split(':');
            languages[key] = parseInt(value);
        });
    return languages;
};

const sum = (array) => {
    if (array.length === 0) {
        return 0;
    }
    return array.reduce((acum, val) => (!isNaN(val) ? acum + val : 0), 0);
};

const saveChart = () => {
    const ctx = document.getElementById('myChart').getContext('2d');
    const tcanvas = document.createElement('canvas');
    const tctx = tcanvas.getContext('2d');
    tcanvas.width = ctx.canvas.width;
    tcanvas.height = ctx.canvas.height;
    tctx.fillStyle = '#0f229e';
    tctx.fillRect(0, 0, tcanvas.width, tcanvas.height);
    tctx.drawImage(document.getElementById('myChart'), 0, 0);

    const image = tcanvas
        .toDataURL('image/png')
        .replace('image/png', 'image/octet-stream');

    const link = document.createElement('a');
    link.download = 'chart.jpg';
    link.href = image;
    link.click();
};

const chart = ({ limit, singleColor }) => {
    const canvasContainer = document.getElementById('canvas-container');
    const color = 'white';
    const username = canvasContainer.dataset.username;
    const languages = JSON.parse(canvasContainer.dataset.languages);
    const colors = singleColor
        ? 'white'
        : JSON.parse(canvasContainer.dataset.colors);
    const keys = Object.keys(languages);
    const values = Object.values(languages);
    if (typeof limit !== 'number') {
        limit = keys.length;
    }
    const labels = keys.slice(0, limit);
    const data = values.slice(0, limit);
    if (keys.length > 20) {
        const valueOthers = sum(values.slice(limit, keys.length));
        labels.push('Others');
        data.push(valueOthers);
    }
    const canvas = document.getElementById('myChart');
    const chart = (window.chart = new Chart(canvas, {
        type: 'bar',
        data: {
            labels,
            datasets: [
                {
                    label: 'Language Stats',
                    backgroundColor: colors,
                    borderColor: 'black',
                    borderWidth: 1,
                    data
                }
            ]
        },
        options: {
            maintainAspectRatio: false,
            legend: {
                display: true,
                labels: {
                    fontColor: color
                },
                position: 'top'
            },
            scales: {
                yAxes: [
                    {
                        ticks: {
                            beginAtZero: true,
                            fontColor: color
                        }
                    }
                ],
                xAxes: [
                    {
                        ticks: {
                            fontColor: color,
                            maxRotation: 90,
                            minRotation: 90
                        }
                    }
                ]
            },
            tooltips: {
                callbacks: {
                    afterBody: () =>
                        `Click to browse repos with the current language by ${username}`
                }
            }
        }
    }));

    const slugify = (text) =>
         text
            .toString()
            .toLowerCase()
            .replace(/\s+/g, "-") // Replace spaces with -
            .replace(/#/g, "%23") // Change all '#' chars with '%23'
            .replace(/\+/g, "%2B") // Replace all '+' with '%2B'
            .replace(/\-\-+/g, "-") // Replace multiple - with single -
            .replace(/^-+/, "") // Trim - from start of text
            .replace(/-+$/, "") // Trim - from end of text
    ;

    const onCanvasClick = (e) => {
        const activeElement = chart.getElementAtEvent(e)[0];

        if (activeElement) {
            let language = chart.data.labels[activeElement._index];
            language = slugify(language);
            const value =
                chart.data.datasets[activeElement._datasetIndex].data[
                    activeElement._index
                ];
            const url = `https://github.com/${username}?tab=repositories&type=&language=${language}`;
            window.open(url, '_blank');
        }
    };
    canvas.addEventListener('click', onCanvasClick);
    return singleColor;
};

const toggleColor = () => {
    window.chart.destroy();
    singleColor = chart({ limit: 20, singleColor: !singleColor });
    const btn = document.getElementById('btn-toggle');

    btn.classList.toggle('active');
    btn.classList.toggle('inactive');
};
<commit_msg>remove colored-bar next to language-state label<commit_after>const parse = (text) => {
    const languages = {};
    text.trim()
        .split(' ')
        .forEach((string) => {
            const [key, value] = string.split(':');
            languages[key] = parseInt(value);
        });
    return languages;
};

const sum = (array) => {
    if (array.length === 0) {
        return 0;
    }
    return array.reduce((acum, val) => (!isNaN(val) ? acum + val : 0), 0);
};

const saveChart = () => {
    const ctx = document.getElementById('myChart').getContext('2d');
    const tcanvas = document.createElement('canvas');
    const tctx = tcanvas.getContext('2d');
    tcanvas.width = ctx.canvas.width;
    tcanvas.height = ctx.canvas.height;
    tctx.fillStyle = '#0f229e';
    tctx.fillRect(0, 0, tcanvas.width, tcanvas.height);
    tctx.drawImage(document.getElementById('myChart'), 0, 0);

    const image = tcanvas
        .toDataURL('image/png')
        .replace('image/png', 'image/octet-stream');

    const link = document.createElement('a');
    link.download = 'chart.jpg';
    link.href = image;
    link.click();
};

const chart = ({ limit, singleColor }) => {
    const canvasContainer = document.getElementById('canvas-container');
    const color = 'white';
    const username = canvasContainer.dataset.username;
    const languages = JSON.parse(canvasContainer.dataset.languages);
    const colors = singleColor
        ? 'white'
        : JSON.parse(canvasContainer.dataset.colors);
    const keys = Object.keys(languages);
    const values = Object.values(languages);
    if (typeof limit !== 'number') {
        limit = keys.length;
    }
    const labels = keys.slice(0, limit);
    const data = values.slice(0, limit);
    if (keys.length > 20) {
        const valueOthers = sum(values.slice(limit, keys.length));
        labels.push('Others');
        data.push(valueOthers);
    }
    const canvas = document.getElementById('myChart');
    const chart = (window.chart = new Chart(canvas, {
        type: 'bar',
        data: {
            labels,
            datasets: [
                {
                    label: 'Language Stats',
                    backgroundColor: colors,
                    borderColor: 'black',
                    borderWidth: 1,
                    data
                }
            ]
        },
        options: {
            maintainAspectRatio: false,
            legend: {
                display: false
            },
            title: {
                display: true,
                text: 'Language Stats',
                fontSize: 16,
                fontColor: '#fff',
                position: 'top'
            },
            scales: {
                yAxes: [
                    {
                        ticks: {
                            beginAtZero: true,
                            fontColor: color
                        }
                    }
                ],
                xAxes: [
                    {
                        ticks: {
                            fontColor: color,
                            maxRotation: 90,
                            minRotation: 90
                        }
                    }
                ]
            },
            tooltips: {
                callbacks: {
                    afterBody: () =>
                        `Click to browse repos with the current language by ${username}`
                }
            }
        }
    }));

    const slugify = (text) =>
        text
            .toString()
            .toLowerCase()
            .replace(/\s+/g, '-') // Replace spaces with -
            .replace(/#/g, '%23') // Change all '#' chars with '%23'
            .replace(/\+/g, '%2B') // Replace all '+' with '%2B'
            .replace(/\-\-+/g, '-') // Replace multiple - with single -
            .replace(/^-+/, '') // Trim - from start of text
            .replace(/-+$/, ''); // Trim - from end of text
    const onCanvasClick = (e) => {
        const activeElement = chart.getElementAtEvent(e)[0];

        if (activeElement) {
            let language = chart.data.labels[activeElement._index];
            language = slugify(language);
            const value =
                chart.data.datasets[activeElement._datasetIndex].data[
                    activeElement._index
                ];
            const url = `https://github.com/${username}?tab=repositories&type=&language=${language}`;
            window.open(url, '_blank');
        }
    };
    canvas.addEventListener('click', onCanvasClick);
    return singleColor;
};

const toggleColor = () => {
    window.chart.destroy();
    singleColor = chart({ limit: 20, singleColor: !singleColor });
    const btn = document.getElementById('btn-toggle');

    btn.classList.toggle('active');
    btn.classList.toggle('inactive');
};
<|endoftext|>


<commit_before>const Gtk = imports.gi.Gtk;

// To use:
// const CssClassMatcher = imports.CssClassMatcher;
// in beforeEach():
// jasmine.addMatchers(CssClassMatcher.customMatchers)

function _descendantHasClass(widget, styleClass) {
    if (widget.get_style_context().has_class(styleClass))
        return true;
    if (widget instanceof Gtk.Container) {
        let children = [];
        // Retrieves internal children as well, widget.get_children() does not
        widget.forall(function (child) {
            children.push(child);
        });
        return children.some(function (child) {
            return _descendantHasClass(child, styleClass);
        });
    }
    return false;
}

const customMatchers = {
    // Usage:
    // expect(widget).toHaveCssClass(Gtk.STYLE_CLASS_LINKED);
    toHaveCssClass: function (util, customEqualityTesters) {
        return {
            compare: function (widget, expected) {
                if (expected === undefined)
                    return { pass: true };

                let result = {
                    pass: widget.get_style_context().has_class(expected)
                };
                if (result.pass)
                    result.message = 'Expected ' + widget + ' not to have CSS class ' + expected + ', but it did';
                else
                    result.message = 'Expected ' + widget + ' to have CSS class ' + expected + ', but it did not';
                return result;
            }
        };
    },

    toHaveDescendantWithCssClass: function (util, customEqualityTesters) {
        return {
            compare: function (widget, expected) {
                if (expected === undefined)
                    return { pass: true };

                let result = {
                    pass: _descendantHasClass(widget, expected)
                };
                if (result.pass)
                    result.message = 'Expected ' + widget + ' not to have a descendant with CSS class ' + expected + ', but it did';
                else
                    result.message = 'Expected ' + widget + ' to have a descendant with CSS class ' + expected + ', but it did not';
                return result;
            }
        };
    }
};
<commit_msg>Don't pass css matchers when matching undefined style class<commit_after>const Gtk = imports.gi.Gtk;

// To use:
// const CssClassMatcher = imports.CssClassMatcher;
// in beforeEach():
// jasmine.addMatchers(CssClassMatcher.customMatchers)

function _descendantHasClass(widget, styleClass) {
    if (widget.get_style_context().has_class(styleClass))
        return true;
    if (widget instanceof Gtk.Container) {
        let children = [];
        // Retrieves internal children as well, widget.get_children() does not
        widget.forall(function (child) {
            children.push(child);
        });
        return children.some(function (child) {
            return _descendantHasClass(child, styleClass);
        });
    }
    return false;
}

const customMatchers = {
    // Usage:
    // expect(widget).toHaveCssClass(Gtk.STYLE_CLASS_LINKED);
    toHaveCssClass: function (util, customEqualityTesters) {
        return {
            compare: function (widget, expected) {
                if (expected === undefined)
                    return { pass: false };

                let result = {
                    pass: widget.get_style_context().has_class(expected)
                };
                if (result.pass)
                    result.message = 'Expected ' + widget + ' not to have CSS class ' + expected + ', but it did';
                else
                    result.message = 'Expected ' + widget + ' to have CSS class ' + expected + ', but it did not';
                return result;
            }
        };
    },

    toHaveDescendantWithCssClass: function (util, customEqualityTesters) {
        return {
            compare: function (widget, expected) {
                if (expected === undefined)
                    return { pass: false };

                let result = {
                    pass: _descendantHasClass(widget, expected)
                };
                if (result.pass)
                    result.message = 'Expected ' + widget + ' not to have a descendant with CSS class ' + expected + ', but it did';
                else
                    result.message = 'Expected ' + widget + ' to have a descendant with CSS class ' + expected + ', but it did not';
                return result;
            }
        };
    }
};
<|endoftext|>


<commit_before>/**
 * Created by lindem on 12/3/13.
 */
function sum (a, b) {
    return a + b;
}

var assert = require('assert'),
    FirstContract = require("./helper/proxy"),
    Contracts = FirstContract.Contracts,
    contractify = FirstContract.contractify,
    c = FirstContract.c,
    helper = require("./helper/helper"),
    throwTest = helper.throwTest,
    noThrowTest = helper.noThrowTest,
    RR_R = c(["R", "R"], "R"),
    RR_Rp = c(["R", "R"], "R+");

suite("contractify() and c() shorthand", function () {
    test("contractify [fail]", function () {
        var f = contractify({params: ["Z+0", "Z+0"], returns: "Z+0" },
            sum);
        throwTest(assert, function () {
            f("a", 2);
        });
    });
    test("contractify [success]", function () {
        var f = contractify(
            { params: ["Z+0", "Z+0"], returns: "Z+0" },
            sum);
        noThrowTest(assert, function () {
            f(1, 2);
        });
    });
    test("c shorthand [fail parameters]", function () {
        throwTest(assert, function () {
            RR_R(sum)("a", "b");
        });
    });
    test("c shorthand [fail return type]", function () {
        throwTest(assert, function () {
            RR_Rp(sum)(1, NaN);
        });
    });
    test("contractify error message [fail]", function () {
        var msg;
        try {
            RR_Rp(sum)(1, NaN);
        } catch (e) {
            msg = e.message;
        }
        assert.ok(/function sum/.test(msg));
        assert.ok(/parameter 2/.test(msg));
    });

});<commit_msg>tested this preservation from c()<commit_after>/**
 * Created by lindem on 12/3/13.
 */
function sum (a, b) {
    return a + b;
}

var assert = require('assert'),
    FirstContract = require("./helper/proxy"),
    Contracts = FirstContract.Contracts,
    contractify = FirstContract.contractify,
    c = FirstContract.c,
    helper = require("./helper/helper"),
    throwTest = helper.throwTest,
    noThrowTest = helper.noThrowTest,
    RR_R = c(["R", "R"], "R"),
    RR_Rp = c(["R", "R"], "R+");

suite("contractify() and c() shorthand", function () {
    test("contractify [fail]", function () {
        var f = contractify({params: ["Z+0", "Z+0"], returns: "Z+0" },
            sum);
        throwTest(assert, function () {
            f("a", 2);
        });
    });
    test("contractify [success]", function () {
        var f = contractify(
            { params: ["Z+0", "Z+0"], returns: "Z+0" },
            sum);
        noThrowTest(assert, function () {
            f(1, 2);
        });
    });
    test("c shorthand [fail parameters]", function () {
        throwTest(assert, function () {
            RR_R(sum)("a", "b");
        });
    });
    test("c shorthand [fail return type]", function () {
        throwTest(assert, function () {
            RR_Rp(sum)(1, NaN);
        });
    });
    test("contractify error message [fail]", function () {
        var msg;
        try {
            RR_Rp(sum)(1, NaN);
        } catch (e) {
            msg = e.message;
        }
        assert.ok(/function sum/.test(msg));
        assert.ok(/parameter 2/.test(msg));
    });
    test("preserve execution context for wrapped function", function () {
        function F() {
            this.foo = "bar";
            return this;
        }
        function baz () {
            return this.foo;
        }

        F.prototype.baz = c([], "S+")(baz);

        noThrowTest(assert, function () {
            var f = new F();
            assert.equal(f.baz(), "bar");
        });
    });

});<|endoftext|>


<commit_before>var sizeOf = require('image-size'),
	Q = require('q'),
	http = require('http'),
	exec = require('child_process').exec,
	fs = require('fs'),
    path = require('path'),
    Base62 = require('base62'),
    port = 8080;

// spawn server
http.createServer(function (req, res) {
	// argument stream: /fetch/filename hash/file name to download as
	var pdfExp = /^\/fetch\/(.*)\/(.*)/;

	// resolve route
	if (req.url === '/create-pdf') {
		// request validation
		validateRequest(req)
			// parse body stream and convert to JSON
			.then(getJsonRequestBody)
			// prepare files and merge
			.then(responseHandler(req, res))
			// success handler
			.then(function (data) {
				outputResponse(res, 200, {
					url: 'http://' + req.headers.host + '/fetch/' + data.hash + '/' + encodeURIComponent(data.name)
				});
			})
			// fail handler
			.catch(function (e) {
				outputResponse(res, 500, {data: e.toString()});
			});
	} else if (pdfExp.test(req.url)) {
		// get path and stream out data
		pdfExists(req.url.match(pdfExp)[1])
			.then(function (path) {
				// get file stat
				var stat = fs.statSync(path);

				// headers
				res.writeHead(200, {
					'Content-Type': 'application/pdf',
					'Content-Length': stat.size,
					// force download the file
					'Content-Disposition': 'attachment; filename="' + (req.url.match(pdfExp)[2]) + '.pdf"'
				});

				// stream out file
				fs.createReadStream(path).pipe(res);
			})
			.catch(function (e) {
				outputResponse(res, 500, {data: e.toString()});
			});
	} else {
		outputResponse(res, 404, {data: 'page does not exist'});
	}
}).listen(port, '0.0.0.0');

/**
 * Validates PDF
 * @todo  probably overkill to use a defer for this, but I may need to do additional checks
 *        in the future if files are off loaded to external server
 * @param  {String} fileName
 * @return {Object} promise
 */
function pdfExists(fileName) {
	var deferred = Q.defer(),
		// construct path
		filePath = path.join(__dirname, 'output', fileName + '.pdf');

	try {
		// check if file exists
		if (!fs.existsSync(filePath)) {
			throw new Error('PDF does not exist');
		}

		deferred.resolve(filePath);
	} catch (e) {
		deferred.reject(e);
	}

	return deferred.promise;
}

/**
 * Outputs HTTP response
 * @param  {Object} res
 * @param  {Number} status
 * @param  {Object} data
 * @return {undefined}
 */
function outputResponse(res, status, data) {
	res.writeHead(status, {
		'Content-Type': 'application/json'
	});

	// resolve to provided response, if non provide generic
	res.end(JSON.stringify(data || {'data': 'OK'}));
}

/**
 * Validates Request
 * @param  {Object} req
 * @return {Object}
 */
function validateRequest(req) {
	var deferred = Q.defer();

	try {
		// post check
		if (req.method !== 'POST') {
			throw new Error('HTTP method must be a POST');
		}

		deferred.resolve(req);
	} catch (e) {
		deferred.reject(e);
	}

	return deferred.promise;
}

/**
 * Parses raw body stream into JSON
 * @param  {Object} req
 * @return {Function}
 */
function getJsonRequestBody(req) {
	var deferred = Q.defer();

	var responseBody = '';

	req.on('data', function (data) {
		responseBody += data;
	});

	req.on('end', function () {
		deferred.resolve(JSON.parse(responseBody));
	});

	return deferred.promise;
}

/**
 * Handles executing PDF actions
 * @param  {Object} req
 * @param  {Object} res
 * @return {Function}
 */
function responseHandler(req, res) {
	return function (data) {
		return prepareFiles(data.inputFiles)
			.then(mergePdfs)
			.then(function (hash) {
				return {
					"name": data.uniquePkgName,
					"hash": hash
				}
			});
	};
}

/**
 * Execute PhantomJS to PDF service
 * Arguments are forwarded onto execution args
 *
 * @return {Object} promise
 */
function phantom() {
	var deferred = Q.defer();

	exec(['phantomjs', __dirname + '/rasterize.js'].concat([].slice.call(arguments)).join(' '), function (err) {
		// error handler
		if (err) {
			console.log('endpoint failed to translate to PDF');
			deferred.reject(err);
		}

		deferred.resolve(arguments[0]); // return output path
	});

	return deferred.promise;
}

/**
 * Translates all files to individual PDFs then moves them to `tmp`
 * @todo  abstract out PDFer and let this resolve moving files
 * @param  {Array} files
 * @return {Object} promise
 */
function prepareFiles(files) {
	console.log('...preparing files for normalization', files);

	function basename(file) {
		return file.substr(0, file.lastIndexOf('.'));
	}

	function isPdf(file) {
		return file.substr(-4) === '.pdf';
	}

	function isHttpProtocol(file) {
		return file.substr(0, 7) === 'http://' || file.substr(0, 8) === 'https://';
	}

    function fileProcess(file) {
        var deferred = Q.defer();

        function resolve(inputPath) {
            deferred.resolve(inputPath);
        }

        // PDF a website
        if (isHttpProtocol(file)) {
            var ouputFilename = 'fieldnote.pdf';

            phantom(file, ouputFilename, '2000px*3000px').then(function () {
                resolve(ouputFilename);
            });

            // if its a PDF, skip it
            // we are assuming all input files are JPG or PNG
        } else if (!isPdf(file)) {
            // configurations
            var sourcePath = __dirname + '/input/' + file,
                outputBase = __dirname + '/tmp/' + basename(file),
                size = sizeOf(sourcePath),
                xAxisLarger = size.width > size.height,
                scale = 2000,
                aspectRatio = xAxisLarger ? (size.width / size.height) : (size.height / size.width),
                errorPx = 5,
                width = (xAxisLarger ? scale : scale / aspectRatio) + errorPx,
                height = (xAxisLarger ? scale / aspectRatio : scale) + errorPx;

            // validate path
            if (!fs.existsSync(sourcePath)) {
                deferred.reject(sourcePath + ' does not exist!');

                // break loop
                return false;
            }

            // create PDF
            phantom(sourcePath, outputBase + '.pdf', width +'px*' + height + 'px').then(function () {
                exec(['pdftk', outputBase + '.pdf', 'cat', '1-1', 'output', outputBase + 'x.pdf'].join (' '), function (err) {
                    // ignore errors for this one, pdftk will fail if there isn't 2 pages
                    if (err) {
                        resolve(outputBase + '.pdf');
                    } else {
                        resolve(outputBase + 'x.pdf');
                        fs.unlinkSync(outputBase + '.pdf');
                    }
                });
            });
        } else {
            // copy it to tmp directory
            fs.createReadStream(__dirname + '/input/' + file)
                .pipe(fs.createWriteStream(__dirname + '/tmp/' + file));

            resolve(__dirname + '/tmp/' + file);
        }

        return deferred.promise;
    }

    return (function (processes) {
        return processes.reduce(Q.when, Q([]));
    }(files.map(function (file) {
        return function (list) {
            var d = Q.defer();

            fileProcess(file).then(function (path) {
                list.push(path);

                d.resolve(list);

                console.log('processed file ' + path + ' on ', new Date);
            }).catch(function () {
                d.reject('file failed');
            });

            return d.promise;
        };
    })));
}

/**
 * Merges PDFs into a single PDF from given list
 * @param  {Array} pdfList
 * @return {Object}  promise
 */
function mergePdfs(pdfList) {
	var deferred = Q.defer(),
		fileName = Base62.encode((new Date).getTime()),
		outputFile = __dirname + '/output/' + fileName + '.pdf';

	console.log('preparing to merge PDFs')

	pdfList.forEach(function (sourcePath) {
		if (!fs.existsSync(sourcePath)) {
			console.log('...  ' + sourcePath + ' does not existxxx!');
		} else {
			console.log('...  ' + sourcePath + ' exists!');
		}
	});

	// merge PDFs
	exec(['pdftk', pdfList.join(' '), 'cat', 'output', outputFile].join(' '), function (err) {
		if (err) {
			console.log('');
			console.log(err);
			deferred.reject(err);
		} else {
			// removes files in tmp
			pdfList.forEach(function (file) {
                try {
				    fs.unlinkSync(file);
                } catch (e) {
                    console.log('could not unlink ' + file);
                }
			});

			console.log('...PDFs merged: ' + outputFile);

			// return hash
			deferred.resolve(fileName);
		}
	});

	return deferred.promise;
}<commit_msg>added support for mulitple http pdfs<commit_after>var sizeOf = require('image-size'),
	Q = require('q'),
	http = require('http'),
	exec = require('child_process').exec,
	fs = require('fs'),
    path = require('path'),
    Base62 = require('base62'),
    port = 8080;

// spawn server
http.createServer(function (req, res) {
	// argument stream: /fetch/filename hash/file name to download as
	var pdfExp = /^\/fetch\/(.*)\/(.*)/;

	// resolve route
	if (req.url === '/create-pdf') {
		// request validation
		validateRequest(req)
			// parse body stream and convert to JSON
			.then(getJsonRequestBody)
			// prepare files and merge
			.then(responseHandler(req, res))
			// success handler
			.then(function (data) {
				outputResponse(res, 200, {
					url: 'http://' + req.headers.host + '/fetch/' + data.hash + '/' + encodeURIComponent(data.name)
				});
			})
			// fail handler
			.catch(function (e) {
				outputResponse(res, 500, {data: e.toString()});
			});
	} else if (pdfExp.test(req.url)) {
		// get path and stream out data
		pdfExists(req.url.match(pdfExp)[1])
			.then(function (path) {
				// get file stat
				var stat = fs.statSync(path);

				// headers
				res.writeHead(200, {
					'Content-Type': 'application/pdf',
					'Content-Length': stat.size,
					// force download the file
					'Content-Disposition': 'attachment; filename="' + (req.url.match(pdfExp)[2]) + '.pdf"'
				});

				// stream out file
				fs.createReadStream(path).pipe(res);
			})
			.catch(function (e) {
				outputResponse(res, 500, {data: e.toString()});
			});
	} else {
		outputResponse(res, 404, {data: 'page does not exist'});
	}
}).listen(port, '0.0.0.0');

/**
 * Validates PDF
 * @todo  probably overkill to use a defer for this, but I may need to do additional checks
 *        in the future if files are off loaded to external server
 * @param  {String} fileName
 * @return {Object} promise
 */
function pdfExists(fileName) {
	var deferred = Q.defer(),
		// construct path
		filePath = path.join(__dirname, 'output', fileName + '.pdf');

	try {
		// check if file exists
		if (!fs.existsSync(filePath)) {
			throw new Error('PDF does not exist');
		}

		deferred.resolve(filePath);
	} catch (e) {
		deferred.reject(e);
	}

	return deferred.promise;
}

/**
 * Outputs HTTP response
 * @param  {Object} res
 * @param  {Number} status
 * @param  {Object} data
 * @return {undefined}
 */
function outputResponse(res, status, data) {
	res.writeHead(status, {
		'Content-Type': 'application/json'
	});

	// resolve to provided response, if non provide generic
	res.end(JSON.stringify(data || {'data': 'OK'}));
}

/**
 * Validates Request
 * @param  {Object} req
 * @return {Object}
 */
function validateRequest(req) {
	var deferred = Q.defer();

	try {
		// post check
		if (req.method !== 'POST') {
			throw new Error('HTTP method must be a POST');
		}

		deferred.resolve(req);
	} catch (e) {
		deferred.reject(e);
	}

	return deferred.promise;
}

/**
 * Parses raw body stream into JSON
 * @param  {Object} req
 * @return {Function}
 */
function getJsonRequestBody(req) {
	var deferred = Q.defer();

	var responseBody = '';

	req.on('data', function (data) {
		responseBody += data;
	});

	req.on('end', function () {
		deferred.resolve(JSON.parse(responseBody));
	});

	return deferred.promise;
}

/**
 * Handles executing PDF actions
 * @param  {Object} req
 * @param  {Object} res
 * @return {Function}
 */
function responseHandler(req, res) {
	return function (data) {
		return prepareFiles(data.inputFiles)
			.then(mergePdfs)
			.then(function (hash) {
				return {
					"name": data.uniquePkgName,
					"hash": hash
				}
			});
	};
}

/**
 * Execute PhantomJS to PDF service
 * Arguments are forwarded onto execution args
 *
 * @return {Object} promise
 */
function phantom() {
	var deferred = Q.defer();

	exec(['phantomjs', __dirname + '/rasterize.js'].concat([].slice.call(arguments)).join(' '), function (err) {
		// error handler
		if (err) {
			console.log('endpoint failed to translate to PDF');
			deferred.reject(err);
		}

		deferred.resolve(arguments[0]); // return output path
	});

	return deferred.promise;
}

/**
 * Translates all files to individual PDFs then moves them to `tmp`
 * @todo  abstract out PDFer and let this resolve moving files
 * @param  {Array} files
 * @return {Object} promise
 */
function prepareFiles(files) {
	console.log('...preparing files for normalization', files);

	function basename(file) {
		return file.substr(0, file.lastIndexOf('.'));
	}

	function isPdf(file) {
		return file.substr(-4) === '.pdf';
	}

	function isHttpProtocol(file) {
		return file.substr(0, 7) === 'http://' || file.substr(0, 8) === 'https://';
	}

    function fileProcess(file) {
        var deferred = Q.defer();

        function resolve(inputPath) {
            deferred.resolve(inputPath);
        }

        // PDF a website
        if (isHttpProtocol(file)) {
            var ouputFilename = guidGenerator() + '.pdf';

            phantom(file, ouputFilename, '2000px*3000px').then(function () {
                resolve(ouputFilename);
            });

            // if its a PDF, skip it
            // we are assuming all input files are JPG or PNG
        } else if (!isPdf(file)) {
            // configurations
            var sourcePath = __dirname + '/input/' + file,
                outputBase = __dirname + '/tmp/' + basename(file),
                size = sizeOf(sourcePath),
                xAxisLarger = size.width > size.height,
                scale = 2000,
                aspectRatio = xAxisLarger ? (size.width / size.height) : (size.height / size.width),
                errorPx = 5,
                width = (xAxisLarger ? scale : scale / aspectRatio) + errorPx,
                height = (xAxisLarger ? scale / aspectRatio : scale) + errorPx;

            // validate path
            if (!fs.existsSync(sourcePath)) {
                deferred.reject(sourcePath + ' does not exist!');

                // break loop
                return false;
            }

            // create PDF
            phantom(sourcePath, outputBase + '.pdf', width +'px*' + height + 'px').then(function () {
                exec(['pdftk', outputBase + '.pdf', 'cat', '1-1', 'output', outputBase + 'x.pdf'].join (' '), function (err) {
                    // ignore errors for this one, pdftk will fail if there isn't 2 pages
                    if (err) {
                        resolve(outputBase + '.pdf');
                    } else {
                        resolve(outputBase + 'x.pdf');
                        fs.unlinkSync(outputBase + '.pdf');
                    }
                });
            });
        } else {
            // copy it to tmp directory
            fs.createReadStream(__dirname + '/input/' + file)
                .pipe(fs.createWriteStream(__dirname + '/tmp/' + file));

            resolve(__dirname + '/tmp/' + file);
        }

        return deferred.promise;
    }

    return (function (processes) {
        return processes.reduce(Q.when, Q([]));
    }(files.map(function (file) {
        return function (list) {
            var d = Q.defer();

            fileProcess(file).then(function (path) {
                list.push(path);

                d.resolve(list);

                console.log('processed file ' + path + ' on ', new Date);
            }).catch(function () {
                d.reject('file failed');
            });

            return d.promise;
        };
    })));
}

/**
 * Merges PDFs into a single PDF from given list
 * @param  {Array} pdfList
 * @return {Object}  promise
 */
function mergePdfs(pdfList) {
	var deferred = Q.defer(),
		fileName = Base62.encode((new Date).getTime()),
		outputFile = __dirname + '/output/' + fileName + '.pdf';

	console.log('preparing to merge PDFs')

	pdfList.forEach(function (sourcePath) {
		if (!fs.existsSync(sourcePath)) {
			console.log('...  ' + sourcePath + ' does not existxxx!');
		} else {
			console.log('...  ' + sourcePath + ' exists!');
		}
	});

	// merge PDFs
	exec(['pdftk', pdfList.join(' '), 'cat', 'output', outputFile].join(' '), function (err) {
		if (err) {
			console.log('');
			console.log(err);
			deferred.reject(err);
		} else {
			// removes files in tmp
			pdfList.forEach(function (file) {
                try {
				    fs.unlinkSync(file);
                } catch (e) {
                    console.log('could not unlink ' + file);
                }
			});

			console.log('...PDFs merged: ' + outputFile);

			// return hash
			deferred.resolve(fileName);
		}
	});

	return deferred.promise;
}

function guidGenerator() {
    var S4 = function() {
        return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
    };
    return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import createStore from './store/createStore';
import AppContainer from './containers/AppContainer';

// ========================================================
// Store Instantiation
// ========================================================
const initialState = window.___INITIAL_STATE__;
const store = createStore(initialState);

// ========================================================
// Render Setup
// ========================================================
const MOUNT_NODE = document.getElementById('root');

let render = () => {
    ReactDOM.render(
        <AppContainer store={store} />,
        MOUNT_NODE
    );
};

// This code is excluded from production bundle
if (__DEV__) {
    if (module.hot) {
    // Development render functions
        const renderApp = render;
        const renderError = (error) => {
            const RedBox = require('redbox-react').default;

            ReactDOM.render(<RedBox error={error} />, MOUNT_NODE);
        };

        // Wrap render in try/catch
        render = () => {
            try {
                renderApp();
            }
            catch (error) {
                console.error(error);
                renderError(error);
            }
        };

        // Setup hot module replacement
        module.hot.accept('./routes/index', () =>
            setImmediate(() => {
                ReactDOM.unmountComponentAtNode(MOUNT_NODE);
                render();
            })
        );
    }
}

// ========================================================
// Go!
// ========================================================
render();
<commit_msg>attempt to revive HMR<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import createStore from './store/createStore';
import AppContainer from './containers/AppContainer';

// ========================================================
// Store Instantiation
// ========================================================
const initialState = window.___INITIAL_STATE__;
const store = createStore(initialState);

// ========================================================
// Render Setup
// ========================================================
const MOUNT_NODE = document.getElementById('root');

let render = () => {
    ReactDOM.render(
        <AppContainer store={store} />,
        MOUNT_NODE
    );
};

// This code is excluded from production bundle
if (__DEV__) {
    if (module.hot) {
    // Development render functions
        const renderApp = render;
        const renderError = (error) => {
            const RedBox = require('redbox-react').default;

            ReactDOM.render(<RedBox error={error} />, MOUNT_NODE);
        };

        // Wrap render in try/catch
        render = () => {
            try {
                renderApp();
            }
            catch (error) {
                console.error(error);
                renderError(error);
            }
        };

        // Setup hot module replacement
        module.hot.accept('./layouts/CoreLayout/index', () =>
            setImmediate(() => {
                ReactDOM.unmountComponentAtNode(MOUNT_NODE);
                render();
            })
        );
    }
}

// ========================================================
// Go!
// ========================================================
render();
<|endoftext|>