<commit_before>//d3.svg.worldMap

import Class from '../base/class'

export default Class.extend({

  init: function(context, conditions) {
    this.context = context;
    this.width = 0;
    this.height = 0;
    this.topOffset = 0;
    this.leftOffset = 0;
    this.bottomOffset = 0;
    this.rightOffset = 0;
    this.fontSize = 0;
    this.fontWidth = 0;
    this.fontHeight = 0;
    this.xAlign = 'center';
    this.yAlign = 'center';
    this.symbols = [];
    if (conditions) {
      this.setConditions(conditions);
    }
  },

  setConditions: function(conditions) {
    if (conditions.rightOffset && !isNaN(parseFloat(conditions.rightOffset)) && isFinite(conditions.rightOffset)) {
      this.rifgtOffset = conditions.rightOffset;
    }
    if (conditions.leftOffset && !isNaN(parseFloat(conditions.leftOffset)) && isFinite(conditions.leftOffset)) {
      this.leftOffset = conditions.leftOffset;
    }
    if (conditions.topOffset && !isNaN(parseFloat(conditions.topOffset)) && isFinite(conditions.topOffset)) {
      this.topOffset = conditions.topOffset;
    }
    if (conditions.bottomOffset && !isNaN(parseFloat(conditions.bottomOffset)) && isFinite(conditions.bottomOffset)) {
      this.bottomOffset = conditions.bottomOffset;
    }
    if (conditions.xAlign) {
      this.xAlign = conditions.xAlign;
    }
    if (conditions.yAlign) {
      this.yAlign = conditions.yAlign;
    }
    return this;
  },

  resize: function(width, height, fontSize, topOffset, leftOffset) {
    this.width = width;
    this.height = height;
    this.fontSize = fontSize;
    if (topOffset) {
      this.topOffset = topOffset;
    }
    if (leftOffset) {
      this.leftOffset = leftOffset;
    }

    var sample = this.context.append("text").text("0").style("font-size", this.fontSize + "px");
    this.fontWidth = sample[0][0].getBBox().width;
    this.fontHeight = this.fontSize*0.72;

    d3.select(sample[0][0]).remove();
    this.__resizeText();
  },

  setText: function(text, resize) {
    var _this = this;
    var newSymbols = text.split('');
    if (newSymbols.length != this.symbols.length) {
      resize = true;
    }
    this.symbols = text.split('');

    this.context.selectAll("text")
      .data(this.symbols).exit().remove();
    this.context.selectAll("text")
      .data(this.symbols)
      .enter()
      .append("text")
      .text(function(d){return d;});

    this.context.selectAll("text").each(function (d, i) {
        d3.select(this).text(d);
    });
    if (resize) {
      return this.__resizeText();
    } else {
      return this;
    }

  },

  __resizeText: function() {
    var _this = this;
    this.context.attr("transform", "translate(" + this.__getLeftOffset() + "," + this.__getTopOffset() + ")");
    this.context.selectAll("text").each(function(d, i) {
        d3.select(this)
          .attr("x", _this.fontWidth * i)
          .style("font-size", _this.fontSize + 'px')
          .style("text-anchor", "middle");
      });
    return this;
  },
  __getLeftOffset: function() {
    switch (this.xAlign) {
      case 'right':
        return this.width - this.fontWidth * this.symbols.length + this.fontWidth/2;
        break;
      case 'left':
        return this.fontWidth/2;
        break;
      default :
        return this.fontWidth/2 + (this.width - this.fontWidth * this.symbols.length)/2;
    }
  },
  __getTopOffset: function() {
    //console.log(this.topOffset);
    switch (this.yAlign) {
      case 'top':
        return this.fontHeight + this.topOffset;
        break;
      case 'bottom':
        return this.height - this.bottomOffset;
        break;
      default :
        return this.fontHeight + (this.height - this.fontHeight)/2 + this.topOffset;
    }
  }

});
<commit_msg>When user resize the browser window the background year shifted behind the axis line<commit_after>//d3.svg.worldMap

import Class from '../base/class'

export default Class.extend({

  init: function(context, conditions) {
    this.context = context;
    this.width = 0;
    this.height = 0;
    this.topOffset = 0;
    this.leftOffset = 0;
    this.bottomOffset = 0;
    this.rightOffset = 0;
    this.fontSize = 0;
    this.fontWidth = 0;
    this.fontHeight = 0;
    this.xAlign = 'center';
    this.yAlign = 'center';
    this.symbols = [];
    if (conditions) {
      this.setConditions(conditions);
    }
  },

  setConditions: function(conditions) {
    if (conditions.rightOffset && !isNaN(parseFloat(conditions.rightOffset)) && isFinite(conditions.rightOffset)) {
      this.rifgtOffset = conditions.rightOffset;
    }
    if (conditions.leftOffset && !isNaN(parseFloat(conditions.leftOffset)) && isFinite(conditions.leftOffset)) {
      this.leftOffset = conditions.leftOffset;
    }
    if (conditions.topOffset && !isNaN(parseFloat(conditions.topOffset)) && isFinite(conditions.topOffset)) {
      this.topOffset = conditions.topOffset;
    }
    if (conditions.bottomOffset && !isNaN(parseFloat(conditions.bottomOffset)) && isFinite(conditions.bottomOffset)) {
      this.bottomOffset = conditions.bottomOffset;
    }
    if (conditions.xAlign) {
      this.xAlign = conditions.xAlign;
    }
    if (conditions.yAlign) {
      this.yAlign = conditions.yAlign;
    }
    return this;
  },

  resize: function(width, height, fontSize, topOffset, leftOffset) {
    this.width = width;
    this.height = height;
    this.fontSize = fontSize;
    if (topOffset) {
      this.topOffset = topOffset;
    }
    if (leftOffset) {
      this.leftOffset = leftOffset;
    }
    if (this.fontSize > this.height) {
      this.fontSize = this.height;
    }
    var sample = this.context.append("text").text("0").style("font-size", this.fontSize + "px");
    this.fontWidth = sample[0][0].getBBox().width;
    this.fontHeight = this.fontSize*0.72;

    d3.select(sample[0][0]).remove();
    this.__resizeText();
  },

  setText: function(text, resize) {
    var _this = this;
    var newSymbols = text.split('');
    if (newSymbols.length != this.symbols.length) {
      resize = true;
    }
    this.symbols = text.split('');

    this.context.selectAll("text")
      .data(this.symbols).exit().remove();
    this.context.selectAll("text")
      .data(this.symbols)
      .enter()
      .append("text")
      .text(function(d){return d;});

    this.context.selectAll("text").each(function (d, i) {
        d3.select(this).text(d);
    });
    if (resize) {
      return this.__resizeText();
    } else {
      return this;
    }

  },

  __resizeText: function() {
    var _this = this;
    this.context.attr("transform", "translate(" + this.__getLeftOffset() + "," + this.__getTopOffset() + ")");
    this.context.selectAll("text").each(function(d, i) {
        d3.select(this)
          .attr("x", _this.fontWidth * i)
          .style("font-size", _this.fontSize + 'px')
          .style("text-anchor", "middle");
      });
    return this;
  },
  __getLeftOffset: function() {
    switch (this.xAlign) {
      case 'right':
        return this.width - this.fontWidth * this.symbols.length + this.fontWidth/2;
        break;
      case 'left':
        return this.fontWidth/2;
        break;
      default :
        return this.fontWidth/2 + (this.width - this.fontWidth * this.symbols.length)/2;
    }
  },
  __getTopOffset: function() {
    //console.log(this.topOffset);
    switch (this.yAlign) {
      case 'top':
        return this.fontHeight + this.topOffset;
        break;
      case 'bottom':
        return this.height - this.bottomOffset;
        break;
      default :
        return this.fontHeight + (this.height - this.fontHeight)/2 + this.topOffset;
    }
  }

});
<|endoftext|>


<commit_before>var util = require('util');
var events = require('events');
var type = require('../data_type');
var log = require('../log');
var Class = require('../class');

module.exports = Base = Class.extend({
  init: function() {
    this.eventEmmiter = new events.EventEmitter();
    for(var n in events.EventEmitter.prototype) {
      this[n] = events.EventEmitter.prototype[n];
    }
  },

  close: function() {
    throw new Error('not yet implemented');
  },

  mapDataType: function(str) {
    switch(str) {
      case type.STRING:
        return 'VARCHAR(255)';
      case type.TEXT:
        return 'TEXT';
      case type.INTEGER:
        return 'INTEGER';
      case type.DATE_TIME:
        return 'INTEGER';
      case type.REAL:
        return 'REAL';
      case type.BLOB:
        return 'BLOB';
      default:
        throw new Error('Invalid data type ' + str);
    }
  },

  createColumnDef: function(name, spec, options) {
    var type       = this.mapDataType(spec.type);
    var len        = spec.length ? util.format('(%s)', spec.length) : '';
    var constraint = this.createColumnConstraint(spec, options);
    return [name, type, len, constraint].join(' ');
  },

  createMigrationsTable: function(callback) {
    var options = {
      columns: {
        'id': { type: type.INTEGER, notNull: true, primaryKey: true, autoIncrement: true },
        'name': { type: type.STRING, length: 255, notNull: true},
        'run_on': { type: type.DATE_TIME, notNull: true}
      },
      ifNotExists: true
    };
    this.createTable('migrations', options, callback);
  },

  createTable: function(tableName, options, callback) {
    var columnSpecs = options;
    var tableOptions = {};

    if (options.columns !== undefined) {
      columnSpecs = options.columns;
      delete options.columns;
      tableOptions = options;
    }

    var ifNotExistsSql = "";
    if(tableOptions.ifNotExists) {
      ifNotExistsSql = "IF NOT EXISTS";
    }

    var primaryKeyColumns = [];
    var columnDefOptions = {
      emitPrimaryKey: false
    };

    for (var columnName in columnSpecs) {
      var columnSpec = this.normalizeColumnSpec(columnSpecs[columnName]);
      columnSpecs[columnName] = columnSpec;
      if (columnSpec.primaryKey) {
        primaryKeyColumns.push(columnName);
      }
    }

    var pkSql = '';
    if (primaryKeyColumns.length > 1) {
      pkSql = util.format(', PRIMARY KEY (%s)', primaryKeyColumns.join(', '));
    } else {
      columnDefOptions.emitPrimaryKey = true;
    }

    var columnDefs = [];
    for (var columnName in columnSpecs) {
      var columnSpec = columnSpecs[columnName];
      columnDefs.push(this.createColumnDef(columnName, columnSpec, columnDefOptions));
    }

    var sql = util.format('CREATE TABLE %s %s (%s%s)', ifNotExistsSql, tableName, columnDefs.join(', '), pkSql);
    this.runSql(sql, callback);
  },

  dropTable: function(tableName, options, callback) {
    if (arguments.length < 3) {
      callback = options;
      options = {};
    }

    var ifExistsSql = '';
    if (options.ifExists) {
      ifExistsSql = 'IF EXISTS';
    }
    var sql = util.format('DROP TABLE %s %s', ifExistsSql, tableName);
    this.runSql(sql, callback);
  },

  renameTable: function(tableName, newTableName, callback) {
    throw new Error('not yet implemented');
  },

  addColumn: function(tableName, columnName, columnSpec, callback) {
    var def = this.createColumnDef(columnName, this.normalizeColumnSpec(columnSpec));
    var sql = util.format('ALTER TABLE %s ADD COLUMN %s', tableName, def);
    this.runSql(sql, callback);
  },

  removeColumn: function(tableName, columnName, callback) {
    throw new Error('not yet implemented');
  },

  renameColumn: function(tableName, oldColumnName, newColumnName, callback) {
    throw new Error('not yet implemented');
  },

  changeColumn: function(tableName, columnName, columnSpec, callback) {
    throw new Error('not yet implemented');
  },

  addIndex: function(tableName, indexName, columns, callback) {
    if (!Array.isArray(columns)) {
      columns = [columns];
    }
    var sql = util.format('CREATE INDEX %s ON %s (%s)', indexName, tableName, columns.join(', '));
    this.runSql(sql, callback);
  },

  insert: function(tableName, columnNameArray, valueArray, callback) {
    if (columnNameArray.length !== valueArray.length) {
      return callback(new Error('The number of columns does not match the number of values.'));
    }

    var sql = util.format('INSERT INTO %s ', tableName);
    var columnNames = '(';
    var values = 'VALUES (';

    for (var index in columnNameArray) {
      columnNames += columnNameArray[index];

      if (typeof(valueArray[index]) === 'string') {
        values += "'" + valueArray[index] + "'";
      } else {
        values += valueArray[index];
      }

      if (index != columnNameArray.length - 1) {
       columnNames += ",";
       values +=  ",";
      }
    }

    sql += columnNames + ') '+ values + ');';
    this.runSql(sql, callback);
  },

  removeIndex: function(indexName, callback) {
    var sql = util.format('DROP INDEX %s', indexName);
    this.runSql(sql, callback);
  },

  normalizeColumnSpec: function(obj) {
    if (typeof(obj) === 'string') {
      return { type: obj };
    } else {
      return obj;
    }
  },

  startMigration: function(cb){cb()},
  endMigration: function(cb){cb()},
  // sql, params, callback
  // sql, callback
  runSql: function() {
    throw new Error('not yet implemented');
  },

  all: function(sql, params, callback) {
    throw new Error('not yet implemented');
  },

  escape: function(str) {
    return str.replace(/'/g, "\'");
  }
});
<commit_msg>Pass through unknown data types unchanged, but issue warning<commit_after>var util = require('util');
var events = require('events');
var type = require('../data_type');
var log = require('../log');
var Class = require('../class');

module.exports = Base = Class.extend({
  init: function() {
    this.eventEmmiter = new events.EventEmitter();
    for(var n in events.EventEmitter.prototype) {
      this[n] = events.EventEmitter.prototype[n];
    }
  },

  close: function() {
    throw new Error('not yet implemented');
  },

  mapDataType: function(str) {
    switch(str) {
      case type.STRING:
        return 'VARCHAR(255)';
      case type.TEXT:
        return 'TEXT';
      case type.INTEGER:
        return 'INTEGER';
      case type.DATE_TIME:
        return 'INTEGER';
      case type.REAL:
        return 'REAL';
      case type.BLOB:
        return 'BLOB';
      default:
        var unknownType = str.toUpperCase();
        log.warn('Using unknown data type', unknownType);
        return unknownType;
    }
  },

  createColumnDef: function(name, spec, options) {
    var type       = this.mapDataType(spec.type);
    var len        = spec.length ? util.format('(%s)', spec.length) : '';
    var constraint = this.createColumnConstraint(spec, options);
    return [name, type, len, constraint].join(' ');
  },

  createMigrationsTable: function(callback) {
    var options = {
      columns: {
        'id': { type: type.INTEGER, notNull: true, primaryKey: true, autoIncrement: true },
        'name': { type: type.STRING, length: 255, notNull: true},
        'run_on': { type: type.DATE_TIME, notNull: true}
      },
      ifNotExists: true
    };
    this.createTable('migrations', options, callback);
  },

  createTable: function(tableName, options, callback) {
    var columnSpecs = options;
    var tableOptions = {};

    if (options.columns !== undefined) {
      columnSpecs = options.columns;
      delete options.columns;
      tableOptions = options;
    }

    var ifNotExistsSql = "";
    if(tableOptions.ifNotExists) {
      ifNotExistsSql = "IF NOT EXISTS";
    }

    var primaryKeyColumns = [];
    var columnDefOptions = {
      emitPrimaryKey: false
    };

    for (var columnName in columnSpecs) {
      var columnSpec = this.normalizeColumnSpec(columnSpecs[columnName]);
      columnSpecs[columnName] = columnSpec;
      if (columnSpec.primaryKey) {
        primaryKeyColumns.push(columnName);
      }
    }

    var pkSql = '';
    if (primaryKeyColumns.length > 1) {
      pkSql = util.format(', PRIMARY KEY (%s)', primaryKeyColumns.join(', '));
    } else {
      columnDefOptions.emitPrimaryKey = true;
    }

    var columnDefs = [];
    for (var columnName in columnSpecs) {
      var columnSpec = columnSpecs[columnName];
      columnDefs.push(this.createColumnDef(columnName, columnSpec, columnDefOptions));
    }

    var sql = util.format('CREATE TABLE %s %s (%s%s)', ifNotExistsSql, tableName, columnDefs.join(', '), pkSql);
    this.runSql(sql, callback);
  },

  dropTable: function(tableName, options, callback) {
    if (arguments.length < 3) {
      callback = options;
      options = {};
    }

    var ifExistsSql = '';
    if (options.ifExists) {
      ifExistsSql = 'IF EXISTS';
    }
    var sql = util.format('DROP TABLE %s %s', ifExistsSql, tableName);
    this.runSql(sql, callback);
  },

  renameTable: function(tableName, newTableName, callback) {
    throw new Error('not yet implemented');
  },

  addColumn: function(tableName, columnName, columnSpec, callback) {
    var def = this.createColumnDef(columnName, this.normalizeColumnSpec(columnSpec));
    var sql = util.format('ALTER TABLE %s ADD COLUMN %s', tableName, def);
    this.runSql(sql, callback);
  },

  removeColumn: function(tableName, columnName, callback) {
    throw new Error('not yet implemented');
  },

  renameColumn: function(tableName, oldColumnName, newColumnName, callback) {
    throw new Error('not yet implemented');
  },

  changeColumn: function(tableName, columnName, columnSpec, callback) {
    throw new Error('not yet implemented');
  },

  addIndex: function(tableName, indexName, columns, callback) {
    if (!Array.isArray(columns)) {
      columns = [columns];
    }
    var sql = util.format('CREATE INDEX %s ON %s (%s)', indexName, tableName, columns.join(', '));
    this.runSql(sql, callback);
  },

  insert: function(tableName, columnNameArray, valueArray, callback) {
    if (columnNameArray.length !== valueArray.length) {
      return callback(new Error('The number of columns does not match the number of values.'));
    }

    var sql = util.format('INSERT INTO %s ', tableName);
    var columnNames = '(';
    var values = 'VALUES (';

    for (var index in columnNameArray) {
      columnNames += columnNameArray[index];

      if (typeof(valueArray[index]) === 'string') {
        values += "'" + valueArray[index] + "'";
      } else {
        values += valueArray[index];
      }

      if (index != columnNameArray.length - 1) {
       columnNames += ",";
       values +=  ",";
      }
    }

    sql += columnNames + ') '+ values + ');';
    this.runSql(sql, callback);
  },

  removeIndex: function(indexName, callback) {
    var sql = util.format('DROP INDEX %s', indexName);
    this.runSql(sql, callback);
  },

  normalizeColumnSpec: function(obj) {
    if (typeof(obj) === 'string') {
      return { type: obj };
    } else {
      return obj;
    }
  },

  startMigration: function(cb){cb()},
  endMigration: function(cb){cb()},
  // sql, params, callback
  // sql, callback
  runSql: function() {
    throw new Error('not yet implemented');
  },

  all: function(sql, params, callback) {
    throw new Error('not yet implemented');
  },

  escape: function(str) {
    return str.replace(/'/g, "\'");
  }
});
<|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:
          'A devtool for inspecting the performance of Vue Components',
        link: 'https://github.com/vue-perf-devtool/vue-perf-devtool#vue-performance-devtool',
        title: 'Vue 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 performance 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, api]
      },
      {
        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]
      },
      {
        description: 'Progressive web metrics at your fingertipz',
        link: 'https://github.com/paulirish/pwmetrics',
        title: 'PWMetrics',
        type: [cli, ci]
      },
      {
        description: 'Google PageSpeed Reports sent directly to your inbox every week',
        link: 'https://pagespeedreports.proteusthemes.com/',
        title: 'PageSpeed Reports',
        type: [external]
      },
      {
        description: 'A tool for speed and SEO optimization checks and guides',
        link: 'https://varvy.com/',
        title: 'Varvy SEO tool and optimization guide',
        type: [external]
      }
    ]
  },
  '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]
      },
      {
        description:
          'A free tool for optimizing JPEG and PNG files',
        link: 'https://tinyjpg.com/',
        title: 'TinyJPG',
        type: [external, api]
      }
    ]
  },
  'video-optimization': {
    title: 'Video optimization',
    subtitle: 'Optimize and compress videos',
    tools: [
      {
        description: 'Convert videos from live sources and resize them on the fly',
        link: 'https://www.ffmpeg.org/ffmpeg.html',
        title: 'ffmpeg',
        type: [cli]
      }
    ]
  },
  '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 application 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]
      },
      {
        description: 'A fast and simple web font loader',
        link: 'https://fontfaceobserver.com/',
        title: 'Font Face Observer',
        type: [dep]
      }
    ]
  },
  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/developit/preact-cli',
        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]
      }
    ]
  },
  'data-analytics': {
    title: 'Data Analytics',
    subtitle: 'Tools that use analytics or machine learning models to improve user experience',
    tools: [
      {
        description: 'Libraries and tools for enabling data-driven user-experiences on the web',
        link: 'https://github.com/guess-js/guess',
        title: 'Guess.js',
        type: [dep]
      }
    ]
  }
};
<commit_msg>Add image-actions<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:
          'A devtool for inspecting the performance of Vue Components',
        link: 'https://github.com/vue-perf-devtool/vue-perf-devtool#vue-performance-devtool',
        title: 'Vue 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 performance 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, api]
      },
      {
        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]
      },
      {
        description: 'Progressive web metrics at your fingertipz',
        link: 'https://github.com/paulirish/pwmetrics',
        title: 'PWMetrics',
        type: [cli, ci]
      },
      {
        description: 'Google PageSpeed Reports sent directly to your inbox every week',
        link: 'https://pagespeedreports.proteusthemes.com/',
        title: 'PageSpeed Reports',
        type: [external]
      },
      {
        description: 'A tool for speed and SEO optimization checks and guides',
        link: 'https://varvy.com/',
        title: 'Varvy SEO tool and optimization guide',
        type: [external]
      }
    ]
  },
  '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: 'A Github Action that automatically compresses JPEGs, PNGs and WebPs in Pull Requests',
        link: 'https://github.com/calibreapp/image-actions/',
        title: 'image-actions',
        type: [ci, cli, external]
      },
      {
        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]
      },
      {
        description:
          'A free tool for optimizing JPEG and PNG files',
        link: 'https://tinyjpg.com/',
        title: 'TinyJPG',
        type: [external, api]
      }
    ]
  },
  'video-optimization': {
    title: 'Video optimization',
    subtitle: 'Optimize and compress videos',
    tools: [
      {
        description: 'Convert videos from live sources and resize them on the fly',
        link: 'https://www.ffmpeg.org/ffmpeg.html',
        title: 'ffmpeg',
        type: [cli]
      }
    ]
  },
  '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 application 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]
      },
      {
        description: 'A fast and simple web font loader',
        link: 'https://fontfaceobserver.com/',
        title: 'Font Face Observer',
        type: [dep]
      }
    ]
  },
  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/developit/preact-cli',
        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]
      }
    ]
  },
  'data-analytics': {
    title: 'Data Analytics',
    subtitle: 'Tools that use analytics or machine learning models to improve user experience',
    tools: [
      {
        description: 'Libraries and tools for enabling data-driven user-experiences on the web',
        link: 'https://github.com/guess-js/guess',
        title: 'Guess.js',
        type: [dep]
      }
    ]
  }
};
<|endoftext|>


<commit_before>var fetchValue = require("../core/fetch/value.js"),
    fetchColor = require("../core/fetch/color.js"),
    fetchText  = require("../core/fetch/text.js")
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Creates correctly formatted tooltip for Apps
//-------------------------------------------------------------------
d3plus.tooltip.app = function(params) {

  if ( !( "d3plus" in params.data ) ) {
    params.data.d3plus = {}
  }

  var vars = params.vars,
      d = params.data,
      dataDepth = "d3plus" in d && "depth" in d.d3plus ? d.d3plus.depth : vars.depth.value,
      ex = params.ex,
      mouse = params.mouseevents ? params.mouseevents : false,
      arrow = "arrow" in params ? params.arrow : true,
      id = fetchValue(vars,d,vars.id.value),
      tooltip_id = params.id || vars.type.value

  if ((d3.event && d3.event.type == "click") && (vars.tooltip.html.value || vars.tooltip.value.long) && !("fullscreen" in params)) {
    var fullscreen = true,
        arrow = false,
        mouse = true,
        length = "long",
        footer = vars.footer.value

    vars.covered = true
  }
  else {
    var fullscreen = false,
        align = params.anchor || vars.tooltip.anchor,
        length = params.length || "short",
        zoom = vars.zoom.direction(d)

    if (zoom === -1) {
      var key = vars.id.nesting[dataDepth-1],
          parent = fetchValue(vars,id,key)
    }

    if (zoom === 1 && vars.zoom.value) {
      var text = vars.format.value(vars.format.locale.value.ui.expand)
    }
    else if (zoom === -1 && vars.zoom.value && vars.history.states.length) {
      var text = vars.format.value(vars.format.locale.value.ui.collapse)
    }
    else if (!vars.small && length == "short" && (vars.tooltip.html.value || vars.tooltip.value.long) && (vars.focus.value.length !== 1 || vars.focus.value[0] != id)) {
      var text = vars.format.locale.value.ui.moreInfo
    }
    else if (length == "long") {
      var text = vars.footer.value || ""
    }
    else {
      var text = ""
    }

    var footer = text.length ? vars.format.value(text,"footer") : false

  }

  if ("x" in params) {
    var x = params.x
  }
  else if (vars.types[vars.type.value].tooltip == "follow") {
    var x = d3.mouse(vars.container.value.node())[0]
  }
  else {
    var x = d.d3plus.x
    if (vars.zoom.translate && vars.zoom.scale) {
      x = vars.zoom.translate[0]+x*vars.zoom.scale
    }
    x += vars.margin.left
  }

  if ("y" in params) {
    var y = params.y
  }
  else if (vars.types[vars.type.value].tooltip == "follow") {
    var y = d3.mouse(vars.container.value.node())[1]
  }
  else {
    var y = d.d3plus.y
    if (vars.zoom.translate && vars.zoom.scale) {
      y = vars.zoom.translate[1]+y*vars.zoom.scale
    }
    y += vars.margin.top
  }

  if ("offset" in params) {
    var offset = params.offset
  }
  else if (vars.types[vars.type.value].tooltip == "follow") {
    var offset = 3
  }
  else {
    var offset = d.d3plus.r ? d.d3plus.r : d.d3plus.height/2
    if (vars.zoom.scale) {
      offset = offset * vars.zoom.scale
    }
  }

  function make_tooltip(html) {

    var ex = {}
      , children = {}
      , depth     = vars.id.nesting[dataDepth+1] in d ? dataDepth+1 : dataDepth
      , nestKey   = vars.id.nesting[depth]
      , nameList  = "merged" in d.d3plus ? d.d3plus.merged : d[nestKey]
      , dataValue = fetchValue( vars , d , vars.size.value )
      , same = (!(nameList instanceof Array) || (nameList instanceof Array && nameList.length === 1)) && depth === vars.depth.value

    if ( !same && vars.tooltip.children.value ) {

      if ( nameList instanceof Array ) {

        nameList = nameList.slice(0)

        if (vars.size.value && d3plus.object.validate(nameList[0])) {

          var namesWithValues = nameList.filter(function(n){
            return vars.size.value in n
          })

          var namesNoValues = nameList.filter(function(n){
            return !(vars.size.value in n)
          })

          d3plus.array.sort( namesWithValues , vars.size.value , "desc" , [] , vars )

          nameList = namesWithValues.concat(namesNoValues)

        }

        var limit = length === "short" ? 3 : vars.data.large
          , max   = d3.min([nameList.length , limit])
          , objs  = []

        for ( var i = 0 ; i < max ; i++ ) {

          var id    = nameList[i]
            , name  = fetchText( vars , id , depth )[0]
            , value = fetchValue( vars , id , vars.size.value , nestKey )
            , color = fetchColor( vars , id , nestKey )

          children[name] = value ? vars.format.value( value , vars.size.value ) : ""

          if ( color ) {
            if ( !children.d3plus_colors ) children.d3plus_colors = {}
            children.d3plus_colors[name] = color
          }

        }

        if ( nameList.length > max ) {
          children.d3plusMore = nameList.length - max
        }

      }
      else if ( nameList && nameList !== "null" ) {

        var name  = fetchText( vars , nameList , depth )[0]

        children[name] = dataValue ? vars.format.value( dataValue , vars.size.value ) : ""

      }

    }

    if ( vars.size.value && vars.tooltip.size.value && dataValue && ( !nameList || nameList instanceof Array ) ) {
      ex[vars.size.value] = dataValue
    }

    var active = vars.active.value ? fetchValue(vars,d,vars.active.value) : d.d3plus.active,
        temp = vars.temp.value ? fetchValue(vars,d,vars.temp.value) : d.d3plus.temp,
        total = vars.total.value ? fetchValue(vars,d,vars.total.value) : d.d3plus.total

    if (typeof active == "number" && active > 0 && total) {
      var label = vars.active.value || "active"
      ex[label] = active+"/"+total+" ("+vars.format.value((active/total)*100,"share")+"%)"
    }

    if (typeof temp == "number" && temp > 0 && total) {
      var label = vars.temp.value || "temp"
      ex[label] = temp+"/"+total+" ("+vars.format.value((temp/total)*100,"share")+"%)"
    }

    if ( vars.tooltip.share.value && d.d3plus.share ) {
      ex.share = vars.format.value(d.d3plus.share*100,"share")+"%"
    }

    var depth = "depth" in params ? params.depth : dataDepth,
        title = params.title || fetchText(vars,d,depth)[0],
        icon = fetchValue(vars,d,vars.icon.value,vars.id.nesting[depth]),
        tooltip_data = d3plus.tooltip.data(vars,d,length,ex,children,depth)

    if (icon === "null") icon = false

    if ((tooltip_data.length > 0 || footer) || ((!d.d3plus_label && length == "short" && title) || (d.d3plus_label && (!("visible" in d.d3plus_label) || ("visible" in d.d3plus_label && d.d3plus_label.visible === false))))) {

      if (!title) {
        title = id
      }

      var depth = "d3plus" in d && "merged" in d.d3plus ? dataDepth - 1 : dataDepth

      if (depth < 0) depth = 0

      depth = vars.id.nesting[depth]

      if (typeof vars.icon.style.value == "string") {
        var icon_style = vars.icon.style.value
      }
      else if (typeof vars.icon.style.value == "object" && vars.icon.style.value[depth]) {
        var icon_style = vars.icon.style.value[depth]
      }
      else {
        var icon_style = "default"
      }

      if (params.width) {
        var width = params.width
      }
      else if (!fullscreen && tooltip_data.length == 0) {
        var width = "auto"
      }
      else {
        var width = vars.tooltip.small
      }

      d3plus.tooltip.create({
        "align": align,
        "arrow": arrow,
        "locale": vars.format.locale.value,
        "background": vars.tooltip.background,
        "curtain": vars.tooltip.curtain.color,
        "curtainopacity": vars.tooltip.curtain.opacity,
        "fontcolor": vars.tooltip.font.color,
        "fontfamily": vars.tooltip.font.family.value,
        "fontsize": vars.tooltip.font.size,
        "fontweight": vars.tooltip.font.weight,
        "data": tooltip_data,
        "color": fetchColor(vars,d),
        "allColors": true,
        "footer": params.footer === false ? params.footer : footer,
        "fullscreen": fullscreen,
        "html": html,
        "icon": icon,
        "id": tooltip_id,
        "max_height": params.maxheight,
        "max_width": vars.tooltip.small,
        "mouseevents": mouse,
        "offset": offset,
        "parent": vars.container.value,
        "style": icon_style,
        "title": title,
        "width": width,
        "x": x,
        "y": y
      })

    }
    else {
      d3plus.tooltip.remove(tooltip_id)
    }

  }

  if (fullscreen) {

    if (typeof vars.tooltip.html.value == "string") {
      make_tooltip(vars.tooltip.html.value)
    }
    else if (typeof vars.tooltip.html.value == "function") {
      make_tooltip(vars.tooltip.html.value(id))
    }
    else if (vars.tooltip.html.value && typeof vars.tooltip.html.value == "object" && vars.tooltip.html.value.url) {
      d3.json(vars.tooltip.html.value.url,function(data){
        var html = vars.tooltip.html.value.callback ? vars.tooltip.html.value.callback(data) : data
        make_tooltip(html)
      })
    }
    else {
      make_tooltip("")
    }

  }
  else {
    make_tooltip("")
  }

}
<commit_msg>fixed size value not adding to tooltip<commit_after>var fetchValue = require("../core/fetch/value.js"),
    fetchColor = require("../core/fetch/color.js"),
    fetchText  = require("../core/fetch/text.js")
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Creates correctly formatted tooltip for Apps
//-------------------------------------------------------------------
d3plus.tooltip.app = function(params) {

  if ( !( "d3plus" in params.data ) ) {
    params.data.d3plus = {}
  }

  var vars = params.vars,
      d = params.data,
      dataDepth = "d3plus" in d && "depth" in d.d3plus ? d.d3plus.depth : vars.depth.value,
      ex = params.ex,
      mouse = params.mouseevents ? params.mouseevents : false,
      arrow = "arrow" in params ? params.arrow : true,
      id = fetchValue(vars,d,vars.id.value),
      tooltip_id = params.id || vars.type.value

  if ((d3.event && d3.event.type == "click") && (vars.tooltip.html.value || vars.tooltip.value.long) && !("fullscreen" in params)) {
    var fullscreen = true,
        arrow = false,
        mouse = true,
        length = "long",
        footer = vars.footer.value

    vars.covered = true
  }
  else {
    var fullscreen = false,
        align = params.anchor || vars.tooltip.anchor,
        length = params.length || "short",
        zoom = vars.zoom.direction(d)

    if (zoom === -1) {
      var key = vars.id.nesting[dataDepth-1],
          parent = fetchValue(vars,id,key)
    }

    if (zoom === 1 && vars.zoom.value) {
      var text = vars.format.value(vars.format.locale.value.ui.expand)
    }
    else if (zoom === -1 && vars.zoom.value && vars.history.states.length) {
      var text = vars.format.value(vars.format.locale.value.ui.collapse)
    }
    else if (!vars.small && length == "short" && (vars.tooltip.html.value || vars.tooltip.value.long) && (vars.focus.value.length !== 1 || vars.focus.value[0] != id)) {
      var text = vars.format.locale.value.ui.moreInfo
    }
    else if (length == "long") {
      var text = vars.footer.value || ""
    }
    else {
      var text = ""
    }

    var footer = text.length ? vars.format.value(text,"footer") : false

  }

  if ("x" in params) {
    var x = params.x
  }
  else if (vars.types[vars.type.value].tooltip == "follow") {
    var x = d3.mouse(vars.container.value.node())[0]
  }
  else {
    var x = d.d3plus.x
    if (vars.zoom.translate && vars.zoom.scale) {
      x = vars.zoom.translate[0]+x*vars.zoom.scale
    }
    x += vars.margin.left
  }

  if ("y" in params) {
    var y = params.y
  }
  else if (vars.types[vars.type.value].tooltip == "follow") {
    var y = d3.mouse(vars.container.value.node())[1]
  }
  else {
    var y = d.d3plus.y
    if (vars.zoom.translate && vars.zoom.scale) {
      y = vars.zoom.translate[1]+y*vars.zoom.scale
    }
    y += vars.margin.top
  }

  if ("offset" in params) {
    var offset = params.offset
  }
  else if (vars.types[vars.type.value].tooltip == "follow") {
    var offset = 3
  }
  else {
    var offset = d.d3plus.r ? d.d3plus.r : d.d3plus.height/2
    if (vars.zoom.scale) {
      offset = offset * vars.zoom.scale
    }
  }

  function make_tooltip(html) {

    var ex = {}
      , children = {}
      , depth     = vars.id.nesting[dataDepth+1] in d ? dataDepth+1 : dataDepth
      , nestKey   = vars.id.nesting[depth]
      , nameList  = "merged" in d.d3plus ? d.d3plus.merged : d[nestKey]
      , dataValue = fetchValue( vars , d , vars.size.value )
      , same = (!(nameList instanceof Array) || (nameList instanceof Array && nameList.length === 1)) && depth === vars.depth.value

    if ( !same && vars.tooltip.children.value ) {

      if ( nameList instanceof Array ) {

        nameList = nameList.slice(0)

        if (vars.size.value && d3plus.object.validate(nameList[0])) {

          var namesWithValues = nameList.filter(function(n){
            return vars.size.value in n
          })

          var namesNoValues = nameList.filter(function(n){
            return !(vars.size.value in n)
          })

          d3plus.array.sort( namesWithValues , vars.size.value , "desc" , [] , vars )

          nameList = namesWithValues.concat(namesNoValues)

        }

        var limit = length === "short" ? 3 : vars.data.large
          , max   = d3.min([nameList.length , limit])
          , objs  = []

        for ( var i = 0 ; i < max ; i++ ) {

          var id    = nameList[i]
            , name  = fetchText( vars , id , depth )[0]
            , value = fetchValue( vars , id , vars.size.value , nestKey )
            , color = fetchColor( vars , id , nestKey )

          children[name] = value ? vars.format.value( value , vars.size.value ) : ""

          if ( color ) {
            if ( !children.d3plus_colors ) children.d3plus_colors = {}
            children.d3plus_colors[name] = color
          }

        }

        if ( nameList.length > max ) {
          children.d3plusMore = nameList.length - max
        }

      }
      else if ( nameList && nameList !== "null" ) {

        var name  = fetchText( vars , nameList , depth )[0]
        children[name] = dataValue ? vars.format.value( dataValue , vars.size.value ) : ""

      }

    }

    if ( vars.size.value && vars.tooltip.size.value && dataValue && ( same || !nameList || nameList instanceof Array ) ) {
      ex[vars.size.value] = dataValue
    }

    var active = vars.active.value ? fetchValue(vars,d,vars.active.value) : d.d3plus.active,
        temp = vars.temp.value ? fetchValue(vars,d,vars.temp.value) : d.d3plus.temp,
        total = vars.total.value ? fetchValue(vars,d,vars.total.value) : d.d3plus.total

    if (typeof active == "number" && active > 0 && total) {
      var label = vars.active.value || "active"
      ex[label] = active+"/"+total+" ("+vars.format.value((active/total)*100,"share")+"%)"
    }

    if (typeof temp == "number" && temp > 0 && total) {
      var label = vars.temp.value || "temp"
      ex[label] = temp+"/"+total+" ("+vars.format.value((temp/total)*100,"share")+"%)"
    }

    if ( vars.tooltip.share.value && d.d3plus.share ) {
      ex.share = vars.format.value(d.d3plus.share*100,"share")+"%"
    }

    var depth = "depth" in params ? params.depth : dataDepth,
        title = params.title || fetchText(vars,d,depth)[0],
        icon = fetchValue(vars,d,vars.icon.value,vars.id.nesting[depth]),
        tooltip_data = d3plus.tooltip.data(vars,d,length,ex,children,depth)

    if (icon === "null") icon = false

    if ((tooltip_data.length > 0 || footer) || ((!d.d3plus_label && length == "short" && title) || (d.d3plus_label && (!("visible" in d.d3plus_label) || ("visible" in d.d3plus_label && d.d3plus_label.visible === false))))) {

      if (!title) {
        title = id
      }

      var depth = "d3plus" in d && "merged" in d.d3plus ? dataDepth - 1 : dataDepth

      if (depth < 0) depth = 0

      depth = vars.id.nesting[depth]

      if (typeof vars.icon.style.value == "string") {
        var icon_style = vars.icon.style.value
      }
      else if (typeof vars.icon.style.value == "object" && vars.icon.style.value[depth]) {
        var icon_style = vars.icon.style.value[depth]
      }
      else {
        var icon_style = "default"
      }

      if (params.width) {
        var width = params.width
      }
      else if (!fullscreen && tooltip_data.length == 0) {
        var width = "auto"
      }
      else {
        var width = vars.tooltip.small
      }

      d3plus.tooltip.create({
        "align": align,
        "arrow": arrow,
        "locale": vars.format.locale.value,
        "background": vars.tooltip.background,
        "curtain": vars.tooltip.curtain.color,
        "curtainopacity": vars.tooltip.curtain.opacity,
        "fontcolor": vars.tooltip.font.color,
        "fontfamily": vars.tooltip.font.family.value,
        "fontsize": vars.tooltip.font.size,
        "fontweight": vars.tooltip.font.weight,
        "data": tooltip_data,
        "color": fetchColor(vars,d),
        "allColors": true,
        "footer": params.footer === false ? params.footer : footer,
        "fullscreen": fullscreen,
        "html": html,
        "icon": icon,
        "id": tooltip_id,
        "max_height": params.maxheight,
        "max_width": vars.tooltip.small,
        "mouseevents": mouse,
        "offset": offset,
        "parent": vars.container.value,
        "style": icon_style,
        "title": title,
        "width": width,
        "x": x,
        "y": y
      })

    }
    else {
      d3plus.tooltip.remove(tooltip_id)
    }

  }

  if (fullscreen) {

    if (typeof vars.tooltip.html.value == "string") {
      make_tooltip(vars.tooltip.html.value)
    }
    else if (typeof vars.tooltip.html.value == "function") {
      make_tooltip(vars.tooltip.html.value(id))
    }
    else if (vars.tooltip.html.value && typeof vars.tooltip.html.value == "object" && vars.tooltip.html.value.url) {
      d3.json(vars.tooltip.html.value.url,function(data){
        var html = vars.tooltip.html.value.callback ? vars.tooltip.html.value.callback(data) : data
        make_tooltip(html)
      })
    }
    else {
      make_tooltip("")
    }

  }
  else {
    make_tooltip("")
  }

}
<|endoftext|>


<commit_before>var $ = require("jquery")
, modalPanel
, CompositeDisposable = require('atom').CompositeDisposable
, ElixirDocs
, ElixirDocsView = require('./elixir-docs-view')
, elixirDocsView
, subscriptions
, Process = require("atom").BufferedProcess
, autocomplete
, iexProcess
, fs = require('fs')
, marked = require('marked')
, inp
, path = require('path')
, projectPaths
, lastError = null
, isNoMixProject = false
, spawn = require('child_process').spawn;
var regs = {
  iex: /^iex\([1-9]{1,}\)>$/,
  start: /^Interactive Elixir/
}
function handleIexResponse(a){
  if(!a){
    return;
  }

  var hasTerminalStatement = a.trim().match(/^(iex\([1-9]{1,}\)>)/);
  var hasInteractiveText= a.indexOf("Interactive Elixir") > -1;
  var hasEshellText = a.indexOf("Eshell") > -1;

  if(!hasTerminalStatement && !hasInteractiveText && !hasEshellText){
    console.log(a);
    updateText(a);
  }

}

function updateText(text){
  var header = "";
  header += "### Elixir Docs \n";
  if(isNoMixProject){
    header += "* standard lib only \n ";
  }
  $('.elixir-docs .focusable-panel').html(marked(header += text));
}

function resizeStarted() {
  $(document).on('mousemove', resizeTreeView);
  return $(document).on('mouseup', resizeStopped);
};

function resizeTreeView(e) {
  $('.elixir-docs').width(window.innerWidth - e.clientX);
};

function resizeStopped() {
  $(document).off('mousemove', resizeTreeView);
  $(document).off('mouseup', resizeStopped);
};

function toggle() {
  !!iexProcess || openTerminal();
  if (modalPanel.isVisible()) {
    return modalPanel.hide();
  } else {
    return modalPanel.show();
  }
}

function findhelp() {
  openTerminal();
  modalPanel.show();
  updateText("");
  var txt = atom.workspace.getActiveTextEditor().getSelectedText();
  var term = "h("+ txt +")\n";
  inp.write(term);
}

function openTerminal(failSafer) {
  var p = atom.project.getPaths()[0] || "";
  var args = !!failSafer ? [] : ["-S", "mix"];
  iexProcess = new Process({
    options: {
      cwd: path.normalize(p)
    },
    command: 'iex',
    args: args,
    stderr: function(e) {
      if(e.indexOf("Could not find a Mix.Project") > -1){
        isNoMixProject = true;
        openTerminal(true);
      } else {

      }
      return lastError = e;
    },
    exit: function(e) {
      atom.notifications.addError("Elixir-docs startup error: \n" + lastError);
      console.error("CLOSED " + e + ", Last Error: " + lastError);
      return "";
    },
    stdout: function() {}
  });

  //atom.notifications.addInfo(editor = atom.workspace.getActivePaneItem().getSelectedText());
  inp = iexProcess.process.stdin;
  iexProcess.process.stdout.on("data", handleIexResponse);
}

module.exports = ElixirDocs = {
  elixirDocsView: elixirDocsView,
  modalPanel: modalPanel,
  subscriptions: subscriptions,
  activate: function (state) {
    console.log(state);
    elixirDocsView = new ElixirDocsView(state.elixirDocsViewState)

    modalPanel = atom.workspace.addRightPanel({
      item: elixirDocsView.getElement(),
      visible: false
    });

    subscriptions = new CompositeDisposable;

    $('.elixir-docs-resize-handle').on('mousedown', resizeStarted);
    subscriptions.add(atom.commands.add('atom-workspace', {'elixir-docs:finddoc': findhelp}));
    subscriptions.add(atom.commands.add('atom-workspace', {'elixir-docs:toggle': toggle}));
    openTerminal();
  },

  deactivate: function() {
    modalPanel.destroy();
    subscriptions.dispose();
    return elixirDocsView.destroy();
  },
  serialize: function() {
    return {
      elixirDocsViewState: elixirDocsView.serialize()
    };
  }
};
<commit_msg>Additional error handling fixes<commit_after>var $ = require("jquery")
, modalPanel
, CompositeDisposable = require('atom').CompositeDisposable
, ElixirDocs
, ElixirDocsView = require('./elixir-docs-view')
, elixirDocsView
, subscriptions
, Process = require("atom").BufferedProcess
, autocomplete
, iexProcess
, fs = require('fs')
, marked = require('marked')
, inp
, path = require('path')
, projectPaths
, lastError = null
, isNoMixProject = false
, spawn = require('child_process').spawn;
var regs = {
  iex: /^iex\([1-9]{1,}\)>$/,
  start: /^Interactive Elixir/
}
function handleIexResponse(a){
  if(!a){
    return;
  }

  var hasTerminalStatement = a.trim().match(/^(iex\([1-9]{1,}\)>)/);
  var hasInteractiveText= a.indexOf("Interactive Elixir") > -1;
  var hasEshellText = a.indexOf("Eshell") > -1;

  if(!hasTerminalStatement && !hasInteractiveText && !hasEshellText){
    console.log(a);
    updateText(a);
  }

}

function updateText(text){
  var header = "";
  header += "### Elixir Docs \n";
  if(isNoMixProject){
    header += "* standard lib only \n ";
  }
  $('.elixir-docs .focusable-panel').html(marked(header += text));
}

function resizeStarted() {
  $(document).on('mousemove', resizeTreeView);
  return $(document).on('mouseup', resizeStopped);
};

function resizeTreeView(e) {
  $('.elixir-docs').width(window.innerWidth - e.clientX);
};

function resizeStopped() {
  $(document).off('mousemove', resizeTreeView);
  $(document).off('mouseup', resizeStopped);
};

function toggle() {
  !!iexProcess || openTerminal();
  updateText("");
  if (modalPanel.isVisible()) {
    return modalPanel.hide();
  } else {
    return modalPanel.show();
  }
}

function findhelp() {
  modalPanel.show();
  updateText("");
  var txt = atom.workspace.getActiveTextEditor().getSelectedText();
  var term = "h("+ txt +")\n";
  inp.write(term);
}

function openTerminal(failSafer) {
  var p = atom.project.getPaths()[0];
  var args = !!failSafer ? [] : ["-S", "mix"];
  iexProcess = new Process({
    options: {
      cwd: path.normalize(p)
    },
    command: 'iex',
    args: args,
    stderr: function(e) {
      if(e.indexOf("Could not find a Mix.Project") > -1){
        console.log(e);
        isNoMixProject = true;
        openTerminal(true);
      } else {
        atom.notifications.addError("Elixir-docs startup error: \n" + e, {dismissable: true});
        lastError = e;
      }
    },
    exit: function(e) {
      console.error("CLOSED " + e + ", Last Error: " + lastError);
      return "";
    },
    stdout: function() {}
  });

  //atom.notifications.addInfo(editor = atom.workspace.getActivePaneItem().getSelectedText());
  inp = iexProcess.process.stdin;
  iexProcess.process.stdout.on("data", handleIexResponse);
}

module.exports = ElixirDocs = {
  elixirDocsView: elixirDocsView,
  modalPanel: modalPanel,
  subscriptions: subscriptions,
  activate: function (state) {
    console.log(state);
    elixirDocsView = new ElixirDocsView(state.elixirDocsViewState)

    modalPanel = atom.workspace.addRightPanel({
      item: elixirDocsView.getElement(),
      visible: false
    });

    subscriptions = new CompositeDisposable;

    $('.elixir-docs-resize-handle').on('mousedown', resizeStarted);
    subscriptions.add(atom.commands.add('atom-workspace', {'elixir-docs:finddoc': findhelp}));
    subscriptions.add(atom.commands.add('atom-workspace', {'elixir-docs:toggle': toggle}));
    openTerminal();
  },

  deactivate: function() {
    modalPanel.destroy();
    subscriptions.dispose();
    return elixirDocsView.destroy();
  },
  serialize: function() {
    return {
      elixirDocsViewState: elixirDocsView.serialize()
    };
  }
};
<|endoftext|>


<commit_before>const {RTM_EVENTS: {MESSAGE}} = require('@slack/client');
const {default: Color} = require('shogi9.js/lib/Color.js');
const {default: Piece} = require('shogi9.js/lib/Piece.js');
const sqlite = require('sqlite');
const path = require('path');
const fs = require('fs');
const {promisify} = require('util');
const minBy = require('lodash/minBy');
const maxBy = require('lodash/maxBy');
const sample = require('lodash/sample');

const {
	serialize,
	deserialize,
	getTransitions,
	charToPiece,
	transitionToText,
} = require('./util.js');
const {upload} = require('./image.js');

const iconUrl = 'https://2.bp.blogspot.com/-UT3sRYCqmLg/WerKjjCzRGI/AAAAAAABHpE/kenNldpvFDI6baHIW0XnB6JzITdh3hB2gCLcBGAs/s400/character_game_syougi.png';

module.exports = ({rtmClient: rtm, webClient: slack}) => {
	const state = {
		previousPosition: null,
		isPrevious打ち歩: false,
		player: null,
		board: null,
		turn: null,
		log: [],
	};

	let match = null;

	const perdon = () => {
		slack.chat.postMessage(process.env.CHANNEL_SANDBOX, ':ha:', {
			username: 'shogi',
			icon_url: iconUrl,
		});
	};

	const post = async (message) => {
		const imageUrl = await upload(state.board);
		await slack.chat.postMessage(process.env.CHANNEL_SANDBOX, message, {
			username: 'shogi',
			icon_url: iconUrl,
			attachments: [
				{
					image_url: imageUrl,
					fallback: state.board.toSFENString(),
				},
			],
		});
	};

	const end = async (color, reason) => {
		const {log} = state;
		state.previousPosition = null;
		state.board = null;
		state.turn = null;
		state.log = [];

		await new Promise((resolve) => setTimeout(resolve, 1000));

		const player = color === Color.Black ? `先手<@${state.player}>` : '後手9マスしょうぎ名人';
		const message = `まで、${log.length}手で${player}の勝ちです。${reason ? `(${reason})` : ''}`;

		await slack.chat.postMessage(process.env.CHANNEL_SANDBOX, message, {
			username: 'shogi',
			icon_url: iconUrl,
		});
	};

	const aiTurn = async () => {
		await new Promise((resolve) => setTimeout(resolve, 2000));

		const inversedBoard = state.board.inverse();

		const currentResult = await sqlite.get(
			'SELECT board, result, depth FROM boards WHERE board = ?',
			serialize(inversedBoard)
		);

		// 先手自殺手
		if (!currentResult) {
			if (state.isPrevious打ち歩) {
				end(Color.Black, '打ち歩詰め');
				return;
			}

			end(Color.White, '王手放置');
			return;
		}

		// 後手詰み
		if (currentResult.depth === 1) {
			end(Color.Black);
			return;
		}

		const transitions = getTransitions(inversedBoard);

		const transitionResults = await sqlite.all(
			`SELECT board, result, depth FROM boards WHERE board IN (${Array(
				transitions.length
			)
				.fill('?')
				.join(', ')})`,
			transitions.map((transition) => serialize(transition.board))
		);

		const loseResults = transitionResults.filter(({result}) => result === 0);
		const winResults = transitionResults.filter(({result}) => result === 1);
		state.isPrevious打ち歩 = false;

		if (loseResults.length > 0) {
			const transition = minBy(loseResults, 'depth');
			state.board = deserialize(transition.board);
			state.turn = Color.Black;
			const logText = transitionToText(transitions.find(({board}) => Buffer.compare(serialize(board), transition.board) === 0));
			state.log.push(logText);
			await post(logText);

			// 先手詰み
			if (transition.depth === 1) {
				end(Color.White);
			}
		} else {
			const transition = maxBy(winResults, 'depth');
			state.board = deserialize(transition.board);
			state.turn = Color.Black;
			const logText = transitionToText(transitions.find(({board}) => Buffer.compare(serialize(board), transition.board) === 0));
			state.log.push(logText);
			await post(logText);
		}
	};

	rtm.on(MESSAGE, async (message) => {
		if (message.channel !== process.env.CHANNEL_SANDBOX) {
			return;
		}

		if (!message.text) {
			return;
		}

		const {text} = message;

		if (text.startsWith('将棋')) {
			const databases = await promisify(fs.readdir)(path.resolve(__dirname, 'boards'));
			await sqlite.open(path.resolve(__dirname, 'boards', sample(databases)));
			const data = await sqlite.get(
				'SELECT * FROM boards WHERE result = 1 AND depth > 3 AND is_good = 1 ORDER BY RANDOM() LIMIT 1'
			);
			state.board = deserialize(data.board);
			state.isPrevious打ち歩 = false;
			state.turn = Color.Black;
			state.player = message.user;

			await post(`${data.depth - 1}手必勝`);
		}

		if (
			(match = text.match(
				/^([123１２３一二三][123１２３一二三]|同)(歩|香|桂|銀|金|飛|角|王|玉|と|成香|成桂|成銀|龍|馬)([右左直]?)([寄引上]?)(成|不成|打)?$/
			))
		) {
			const [, position, pieceChar, xFlag, yFlag, promoteFlag] = match;
			const piece = charToPiece(pieceChar);

			if (state.board === null || state.turn !== Color.Black) {
				perdon();
				return;
			}

			if (position === '同' && state.previousPosition === null) {
				perdon();
				return;
			}

			const x =
				['1１一', '2２二', '3３三'].findIndex((chars) =>
					chars.includes(position.charAt(0))) + 1;
			const y =
				['1１一', '2２二', '3３三'].findIndex((chars) =>
					chars.includes(position.charAt(1))) + 1;

			if (promoteFlag !== '打') {
				const moves = state.board.getMovesTo(x, y, piece, Color.Black);
				if (moves.length > 0) {
					const move = moves[0];
					const isPromotable =
						(move.from.y === 1 || move.to.y === 1) && Piece.canPromote(piece);

					if (isPromotable && promoteFlag === undefined) {
						perdon();
						return;
					}

					state.board.move(
						move.from.x,
						move.from.y,
						move.to.x,
						move.to.y,
						isPromotable && promoteFlag === '成'
					);

					state.turn = Color.White;
					state.isPrevious打ち歩 = false;
					const logText = `☗${'123'[x - 1]}${'一二三'[y - 1]}${pieceChar}`;
					state.log.push(logText);

					await post(logText);

					aiTurn();

					return;
				}
			}

			const hands = state.board
				.getDropsBy(Color.Black)
				.filter(({to, kind}) => to.x === x && to.y === y && kind === piece);
			if (hands.length > 0) {
				state.board.drop(x, y, piece, Color.Black);

				state.turn = Color.White;
				state.isPrevious打ち歩 = piece === 'FU';
				const logText = `☗${'123'[x - 1]}${'一二三'[y - 1]}${pieceChar}`;
				state.log.push(logText);

				await post(logText);

				aiTurn();

				return;
			}

			perdon();
		}

		if (text === '負けました') {
			if (state.board === null || state.turn !== Color.Black) {
				perdon();
				return;
			}

			end(Color.White);
		}
	});
};
<commit_msg>Fix 打ち歩詰め detection<commit_after>const {RTM_EVENTS: {MESSAGE}} = require('@slack/client');
const {default: Color} = require('shogi9.js/lib/Color.js');
const {default: Piece} = require('shogi9.js/lib/Piece.js');
const sqlite = require('sqlite');
const path = require('path');
const fs = require('fs');
const {promisify} = require('util');
const minBy = require('lodash/minBy');
const maxBy = require('lodash/maxBy');
const sample = require('lodash/sample');

const {
	serialize,
	deserialize,
	getTransitions,
	charToPiece,
	transitionToText,
} = require('./util.js');
const {upload} = require('./image.js');

const iconUrl = 'https://2.bp.blogspot.com/-UT3sRYCqmLg/WerKjjCzRGI/AAAAAAABHpE/kenNldpvFDI6baHIW0XnB6JzITdh3hB2gCLcBGAs/s400/character_game_syougi.png';

module.exports = ({rtmClient: rtm, webClient: slack}) => {
	const state = {
		previousPosition: null,
		isPrevious打ち歩: false,
		player: null,
		board: null,
		turn: null,
		log: [],
	};

	let match = null;

	const perdon = () => {
		slack.chat.postMessage(process.env.CHANNEL_SANDBOX, ':ha:', {
			username: 'shogi',
			icon_url: iconUrl,
		});
	};

	const post = async (message) => {
		const imageUrl = await upload(state.board);
		await slack.chat.postMessage(process.env.CHANNEL_SANDBOX, message, {
			username: 'shogi',
			icon_url: iconUrl,
			attachments: [
				{
					image_url: imageUrl,
					fallback: state.board.toSFENString(),
				},
			],
		});
	};

	const end = async (color, reason) => {
		const {log} = state;
		state.previousPosition = null;
		state.board = null;
		state.turn = null;
		state.log = [];

		await new Promise((resolve) => setTimeout(resolve, 1000));

		const player = color === Color.Black ? `先手<@${state.player}>` : '後手9マスしょうぎ名人';
		const message = `まで、${log.length}手で${player}の勝ちです。${reason ? `(${reason})` : ''}`;

		await slack.chat.postMessage(process.env.CHANNEL_SANDBOX, message, {
			username: 'shogi',
			icon_url: iconUrl,
		});
	};

	const aiTurn = async () => {
		await new Promise((resolve) => setTimeout(resolve, 2000));

		const inversedBoard = state.board.inverse();

		const currentResult = await sqlite.get(
			'SELECT board, result, depth FROM boards WHERE board = ?',
			serialize(inversedBoard)
		);

		// 先手自殺手
		if (!currentResult) {
			end(Color.White, '王手放置');
			return;
		}

		// 後手詰み
		if (currentResult.depth === 1) {
			if (state.isPrevious打ち歩) {
				end(Color.White, '打ち歩詰め');
				return;
			}

			end(Color.Black);
			return;
		}

		const transitions = getTransitions(inversedBoard);

		const transitionResults = await sqlite.all(
			`SELECT board, result, depth FROM boards WHERE board IN (${Array(
				transitions.length
			)
				.fill('?')
				.join(', ')})`,
			transitions.map((transition) => serialize(transition.board))
		);

		const loseResults = transitionResults.filter(({result}) => result === 0);
		const winResults = transitionResults.filter(({result}) => result === 1);
		state.isPrevious打ち歩 = false;

		if (loseResults.length > 0) {
			const transition = minBy(loseResults, 'depth');
			state.board = deserialize(transition.board);
			state.turn = Color.Black;
			const logText = transitionToText(transitions.find(({board}) => Buffer.compare(serialize(board), transition.board) === 0));
			state.log.push(logText);
			await post(logText);

			// 先手詰み
			if (transition.depth === 1) {
				end(Color.White);
			}
		} else {
			const transition = maxBy(winResults, 'depth');
			state.board = deserialize(transition.board);
			state.turn = Color.Black;
			const logText = transitionToText(transitions.find(({board}) => Buffer.compare(serialize(board), transition.board) === 0));
			state.log.push(logText);
			await post(logText);
		}
	};

	rtm.on(MESSAGE, async (message) => {
		if (message.channel !== process.env.CHANNEL_SANDBOX) {
			return;
		}

		if (!message.text) {
			return;
		}

		const {text} = message;

		if (text.startsWith('将棋')) {
			const databases = await promisify(fs.readdir)(path.resolve(__dirname, 'boards'));
			await sqlite.open(path.resolve(__dirname, 'boards', sample(databases)));
			const data = await sqlite.get(
				'SELECT * FROM boards WHERE result = 1 AND depth > 3 AND is_good = 1 ORDER BY RANDOM() LIMIT 1'
			);
			state.board = deserialize(data.board);
			state.isPrevious打ち歩 = false;
			state.turn = Color.Black;
			state.player = message.user;

			await post(`${data.depth - 1}手必勝`);
		}

		if (
			(match = text.match(
				/^([123１２３一二三][123１２３一二三]|同)(歩|香|桂|銀|金|飛|角|王|玉|と|成香|成桂|成銀|龍|馬)([右左直]?)([寄引上]?)(成|不成|打)?$/
			))
		) {
			const [, position, pieceChar, xFlag, yFlag, promoteFlag] = match;
			const piece = charToPiece(pieceChar);

			if (state.board === null || state.turn !== Color.Black) {
				perdon();
				return;
			}

			if (position === '同' && state.previousPosition === null) {
				perdon();
				return;
			}

			const x =
				['1１一', '2２二', '3３三'].findIndex((chars) =>
					chars.includes(position.charAt(0))) + 1;
			const y =
				['1１一', '2２二', '3３三'].findIndex((chars) =>
					chars.includes(position.charAt(1))) + 1;

			if (promoteFlag !== '打') {
				const moves = state.board.getMovesTo(x, y, piece, Color.Black);
				if (moves.length > 0) {
					const move = moves[0];
					const isPromotable =
						(move.from.y === 1 || move.to.y === 1) && Piece.canPromote(piece);

					if (isPromotable && promoteFlag === undefined) {
						perdon();
						return;
					}

					state.board.move(
						move.from.x,
						move.from.y,
						move.to.x,
						move.to.y,
						isPromotable && promoteFlag === '成'
					);

					state.turn = Color.White;
					state.isPrevious打ち歩 = false;
					const logText = `☗${'123'[x - 1]}${'一二三'[y - 1]}${pieceChar}`;
					state.log.push(logText);

					await post(logText);

					aiTurn();

					return;
				}
			}

			const hands = state.board
				.getDropsBy(Color.Black)
				.filter(({to, kind}) => to.x === x && to.y === y && kind === piece);
			if (hands.length > 0) {
				state.board.drop(x, y, piece, Color.Black);

				state.turn = Color.White;
				state.isPrevious打ち歩 = piece === 'FU';
				const logText = `☗${'123'[x - 1]}${'一二三'[y - 1]}${pieceChar}`;
				state.log.push(logText);

				await post(logText);

				aiTurn();

				return;
			}

			perdon();
		}

		if (text === '負けました') {
			if (state.board === null || state.turn !== Color.Black) {
				perdon();
				return;
			}

			end(Color.White);
		}
	});
};
<|endoftext|>


<commit_before>var classNames = require('classnames');
var React = require('react');

import StringUtil from '../utils/StringUtil';

var FilterHeadline = React.createClass({

  displayName: 'FilterHeadline',

  propTypes: {
    onReset: React.PropTypes.func.isRequired,
    name: React.PropTypes.string.isRequired,
    currentLength: React.PropTypes.number.isRequired,
    totalLength: React.PropTypes.number.isRequired,
    inverseStyle: React.PropTypes.bool
  },

  getDefaultProps: function () {
    return {
      inverseStyle: false
    };
  },

  handleReset: function (e) {
    e.preventDefault();
    this.props.onReset();
  },

  render: function () {
    var filteredLength = this.props.currentLength;
    var totalLength = this.props.totalLength;
    let inverseStyle = this.props.inverseStyle;
    let name = StringUtil.pluralize(this.props.name, totalLength);

    var filteredClassSet = classNames({
      'h4': true,
      'inverse': inverseStyle,
      'hidden': filteredLength === totalLength
    });

    var unfilteredClassSet = classNames({
      'h4': true,
      'inverse': inverseStyle,
      'hidden': filteredLength !== totalLength
    });

    var anchorClassSet = classNames({
      'h4 clickable': true,
      'inverse': inverseStyle,
      'hidden': filteredLength === totalLength
    });

    let listClassSet = classNames({
      'list-unstyled list-inline': true,
      'inverse': inverseStyle
    });

    return (

          Showing {filteredLength} of {totalLength} {name}

            (Show all)

          {totalLength} {name}

    );
  }
});

module.exports = FilterHeadline;
<commit_msg>Bring FilterHeadline component to es6<commit_after>import React, {PropTypes} from 'react';
import classNames from 'classnames';

import StringUtil from '../utils/StringUtil';

const METHODS_TO_BIND = [
  'handleReset'
];

class FilterHeadline extends React.Component {
  constructor() {
    super();

    METHODS_TO_BIND.forEach((method) => {
      this[method] = this[method].bind(this);
    });
  }

  handleReset(e) {
    e.preventDefault();
    this.props.onReset();
  }

  render() {
    let {currentLength, inverseStyle, name, totalLength} = this.props;
    name = StringUtil.pluralize(name, totalLength);

    let filteredClassSet = classNames({
      'h4': true,
      'inverse': inverseStyle,
      'hidden': filteredLength === totalLength
    });

    let unfilteredClassSet = classNames({
      'h4': true,
      'inverse': inverseStyle,
      'hidden': filteredLength !== totalLength
    });

    let anchorClassSet = classNames({
      'h4 clickable': true,
      'inverse': inverseStyle,
      'hidden': filteredLength === totalLength
    });

    let listClassSet = classNames({
      'list-unstyled list-inline': true,
      'inverse': inverseStyle
    });

    return (

          Showing {currentLength} of {totalLength} {name}

            (Show all)

          {totalLength} {name}

    );
  }
}

FilterHeadline.defaultProps = {
  inverseStyle: false
};

FilterHeadline.propTypes = {
  currentLength: PropTypes.number.isRequired,
  inverseStyle: PropTypes.bool,
  name: PropTypes.string.isRequired,
  onReset: PropTypes.func.isRequired,
  totalLength: PropTypes.number.isRequired
};

module.exports = FilterHeadline;
<|endoftext|>


<commit_before>export default [
  'scanCode',
  'setScreenBrightness',
  'getScreenBrightness',
  'setKeepScreenOn',
  'addPhoneContact',
  'subscribePush',
  'unsubscribePush',
  'onPush',
  'offPush',
  'saveImageToPhotosAlbum',
  'saveVideoToPhotosAlbum',
  'setClipboardData',
  'getClipboardData',
  'getRecorderManager',
  'login',
  'share',
  'requestPayment'
]
<commit_msg>fix: 补充部分不支持的 API<commit_after>export default [
  'scanCode',
  'setScreenBrightness',
  'getScreenBrightness',
  'setKeepScreenOn',
  'addPhoneContact',
  'saveImageToPhotosAlbum',
  'saveVideoToPhotosAlbum',
  'setClipboardData',
  'getClipboardData',
  'getRecorderManager',
  'getProvider',
  'login',
  'getUserInfo',
  'share',
  'requestPayment',
  'subscribePush',
  'unsubscribePush',
  'onPush',
  'offPush'
]
<|endoftext|>