<commit_before>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

'use strict';

/* eslint-disable no-for-of-loops/no-for-of-loops */

// Hi, if this is your first time editing/reading a Dangerfile, here's a summary:
// It's a JS runtime which helps you provide continuous feedback inside GitHub.
//
// You can see the docs here: http://danger.systems/js/
//
// If you want to test changes Danger, I'd recommend checking out an existing PR
// and then running the `danger pr` command.
//
// You'll need a GitHub token, you can re-use this one:
//
//  0a7d5c3cad9a6dbec2d9 9a5222cf49062a4c1ef7
//
// (Just remove the space)
//
// So, for example:
//
// `DANGER_GITHUB_API_TOKEN=[ENV_ABOVE] yarn danger pr https://github.com/facebook/react/pull/11865

const {markdown, danger, warn} = require('danger');
const {promisify} = require('util');
const glob = promisify(require('glob'));
const gzipSize = require('gzip-size');

const {readFileSync, statSync} = require('fs');

const BASE_DIR = 'base-build';
const HEAD_DIR = 'build';

const CRITICAL_THRESHOLD = 0.02;
const SIGNIFICANCE_THRESHOLD = 0.002;
const CRITICAL_ARTIFACT_PATHS = new Set([
  // We always report changes to these bundles, even if the change is
  // insiginificant or non-existent.
  'oss-stable/react-dom/cjs/react-dom.production.min.js',
  'oss-experimental/react-dom/cjs/react-dom.production.min.js',
  'facebook-www/ReactDOM-prod.classic.js',
  'facebook-www/ReactDOM-prod.modern.js',
  'facebook-www/ReactDOMForked-prod.classic.js',
]);

const kilobyteFormatter = new Intl.NumberFormat('en', {
  style: 'unit',
  unit: 'kilobyte',
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
});

function kbs(bytes) {
  return kilobyteFormatter.format(bytes / 1000);
}

const percentFormatter = new Intl.NumberFormat('en', {
  style: 'percent',
  signDisplay: 'exceptZero',
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
});

function change(decimal) {
  if (Number === Infinity) {
    return 'New file';
  }
  if (decimal === -1) {
    return 'Deleted';
  }
  if (decimal < 0.0001) {
    return '=';
  }
  return percentFormatter.format(decimal);
}

const header = `
  | Name | +/- | Base | Current | +/- gzip | Base gzip | Current gzip |
  | ---- | --- | ---- | ------- | -------- | --------- | ------------ |`;

function row(result) {
  // prettier-ignore
  return `| ${result.path} | **${change(result.change)}** | ${kbs(result.baseSize)} | ${kbs(result.headSize)} | ${change(result.changeGzip)} | ${kbs(result.baseSizeGzip)} | ${kbs(result.headSizeGzip)}`;
}

(async function() {
  // Use git locally to grab the commit which represents the place
  // where the branches differ

  const upstreamRepo = danger.github.pr.base.repo.full_name;
  if (upstreamRepo !== 'facebook/react') {
    // Exit unless we're running in the main repo
    return;
  }

  let headSha;
  let baseSha;
  try {
    headSha = String(readFileSync(HEAD_DIR + '/COMMIT_SHA')).trim();
    baseSha = String(readFileSync(BASE_DIR + '/COMMIT_SHA')).trim();
  } catch {
    warn(
      "Failed to read build artifacts. It's possible a build configuration " +
        'has changed upstream. Try pulling the latest changes from the ' +
        'main branch.'
    );
    return;
  }

  // Disable sizeBot in a Devtools Pull Request. Because that doesn't affect production bundle size.
  const commitFiles = [
    ...danger.git.created_files,
    ...danger.git.deleted_files,
    ...danger.git.modified_files,
  ];
  if (
    commitFiles.every(filename => filename.includes('packages/react-devtools'))
  )
    return;

  const resultsMap = new Map();

  // Find all the head (current) artifacts paths.
  const headArtifactPaths = await glob('**/*.js', {cwd: 'build'});
  for (const artifactPath of headArtifactPaths) {
    try {
      // This will throw if there's no matching base artifact
      const baseSize = statSync(BASE_DIR + '/' + artifactPath).size;
      const baseSizeGzip = gzipSize.fileSync(BASE_DIR + '/' + artifactPath);

      const headSize = statSync(HEAD_DIR + '/' + artifactPath).size;
      const headSizeGzip = gzipSize.fileSync(HEAD_DIR + '/' + artifactPath);
      resultsMap.set(artifactPath, {
        path: artifactPath,
        headSize,
        headSizeGzip,
        baseSize,
        baseSizeGzip,
        change: (headSize - baseSize) / baseSize,
        changeGzip: (headSizeGzip - baseSizeGzip) / baseSizeGzip,
      });
    } catch {
      // There's no matching base artifact. This is a new file.
      const baseSize = 0;
      const baseSizeGzip = 0;
      const headSize = statSync(HEAD_DIR + '/' + artifactPath).size;
      const headSizeGzip = gzipSize.fileSync(HEAD_DIR + '/' + artifactPath);
      resultsMap.set(artifactPath, {
        path: artifactPath,
        headSize,
        headSizeGzip,
        baseSize,
        baseSizeGzip,
        change: Infinity,
        changeGzip: Infinity,
      });
    }
  }

  // Check for base artifacts that were deleted in the head.
  const baseArtifactPaths = await glob('**/*.js', {cwd: 'base-build'});
  for (const artifactPath of baseArtifactPaths) {
    if (!resultsMap.has(artifactPath)) {
      const baseSize = statSync(BASE_DIR + '/' + artifactPath).size;
      const baseSizeGzip = gzipSize.fileSync(BASE_DIR + '/' + artifactPath);
      const headSize = 0;
      const headSizeGzip = 0;
      resultsMap.set(artifactPath, {
        path: artifactPath,
        headSize,
        headSizeGzip,
        baseSize,
        baseSizeGzip,
        change: -1,
        changeGzip: -1,
      });
    }
  }

  const results = Array.from(resultsMap.values());
  results.sort((a, b) => b.change - a.change);

  let criticalResults = [];
  for (const artifactPath of CRITICAL_ARTIFACT_PATHS) {
    const result = resultsMap.get(artifactPath);
    if (result === undefined) {
      throw new Error(
        'Missing expected bundle. If this was an intentional change to the ' +
          'build configuration, update Dangerfile.js accordingly: ' +
          artifactPath
      );
    }
    criticalResults.push(row(result));
  }

  let significantResults = [];
  for (const result of results) {
    // If result exceeds critical threshold, add to top section.
    if (
      (result.change > CRITICAL_THRESHOLD ||
        0 - result.change > CRITICAL_THRESHOLD ||
        // New file
        result.change === Infinity ||
        // Deleted file
        result.change === -1) &&
      // Skip critical artifacts. We added those earlier, in a fixed order.
      !CRITICAL_ARTIFACT_PATHS.has(result.path)
    ) {
      criticalResults.push(row(result));
    }

    // Do the same for results that exceed the significant threshold. These
    // will go into the bottom, collapsed section. Intentionally including
    // critical artifacts in this section, too.
    if (
      result.change > SIGNIFICANCE_THRESHOLD ||
      0 - result.change > SIGNIFICANCE_THRESHOLD ||
      result.change === Infinity ||
      result.change === -1
    ) {
      significantResults.push(row(result));
    }
  }

  markdown(`
Comparing: ${baseSha}...${headSha}

## Critical size changes

Includes critical production bundles, as well as any change greater than ${CRITICAL_THRESHOLD *
    100}%:

${header}
${criticalResults.join('\n')}

## Significant size changes

Includes any change greater than ${SIGNIFICANCE_THRESHOLD * 100}%:

${
  significantResults.length > 0
    ? `

Expand to show
${header}
${significantResults.join('\n')}

`
    : '(No significant changes)'
}
`);
})();
<commit_msg>Fix typo in dangerfile.js (insiginificant -> insignificant) (#24393)<commit_after>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

'use strict';

/* eslint-disable no-for-of-loops/no-for-of-loops */

// Hi, if this is your first time editing/reading a Dangerfile, here's a summary:
// It's a JS runtime which helps you provide continuous feedback inside GitHub.
//
// You can see the docs here: http://danger.systems/js/
//
// If you want to test changes Danger, I'd recommend checking out an existing PR
// and then running the `danger pr` command.
//
// You'll need a GitHub token, you can re-use this one:
//
//  0a7d5c3cad9a6dbec2d9 9a5222cf49062a4c1ef7
//
// (Just remove the space)
//
// So, for example:
//
// `DANGER_GITHUB_API_TOKEN=[ENV_ABOVE] yarn danger pr https://github.com/facebook/react/pull/11865

const {markdown, danger, warn} = require('danger');
const {promisify} = require('util');
const glob = promisify(require('glob'));
const gzipSize = require('gzip-size');

const {readFileSync, statSync} = require('fs');

const BASE_DIR = 'base-build';
const HEAD_DIR = 'build';

const CRITICAL_THRESHOLD = 0.02;
const SIGNIFICANCE_THRESHOLD = 0.002;
const CRITICAL_ARTIFACT_PATHS = new Set([
  // We always report changes to these bundles, even if the change is
  // insignificant or non-existent.
  'oss-stable/react-dom/cjs/react-dom.production.min.js',
  'oss-experimental/react-dom/cjs/react-dom.production.min.js',
  'facebook-www/ReactDOM-prod.classic.js',
  'facebook-www/ReactDOM-prod.modern.js',
  'facebook-www/ReactDOMForked-prod.classic.js',
]);

const kilobyteFormatter = new Intl.NumberFormat('en', {
  style: 'unit',
  unit: 'kilobyte',
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
});

function kbs(bytes) {
  return kilobyteFormatter.format(bytes / 1000);
}

const percentFormatter = new Intl.NumberFormat('en', {
  style: 'percent',
  signDisplay: 'exceptZero',
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
});

function change(decimal) {
  if (Number === Infinity) {
    return 'New file';
  }
  if (decimal === -1) {
    return 'Deleted';
  }
  if (decimal < 0.0001) {
    return '=';
  }
  return percentFormatter.format(decimal);
}

const header = `
  | Name | +/- | Base | Current | +/- gzip | Base gzip | Current gzip |
  | ---- | --- | ---- | ------- | -------- | --------- | ------------ |`;

function row(result) {
  // prettier-ignore
  return `| ${result.path} | **${change(result.change)}** | ${kbs(result.baseSize)} | ${kbs(result.headSize)} | ${change(result.changeGzip)} | ${kbs(result.baseSizeGzip)} | ${kbs(result.headSizeGzip)}`;
}

(async function() {
  // Use git locally to grab the commit which represents the place
  // where the branches differ

  const upstreamRepo = danger.github.pr.base.repo.full_name;
  if (upstreamRepo !== 'facebook/react') {
    // Exit unless we're running in the main repo
    return;
  }

  let headSha;
  let baseSha;
  try {
    headSha = String(readFileSync(HEAD_DIR + '/COMMIT_SHA')).trim();
    baseSha = String(readFileSync(BASE_DIR + '/COMMIT_SHA')).trim();
  } catch {
    warn(
      "Failed to read build artifacts. It's possible a build configuration " +
        'has changed upstream. Try pulling the latest changes from the ' +
        'main branch.'
    );
    return;
  }

  // Disable sizeBot in a Devtools Pull Request. Because that doesn't affect production bundle size.
  const commitFiles = [
    ...danger.git.created_files,
    ...danger.git.deleted_files,
    ...danger.git.modified_files,
  ];
  if (
    commitFiles.every(filename => filename.includes('packages/react-devtools'))
  )
    return;

  const resultsMap = new Map();

  // Find all the head (current) artifacts paths.
  const headArtifactPaths = await glob('**/*.js', {cwd: 'build'});
  for (const artifactPath of headArtifactPaths) {
    try {
      // This will throw if there's no matching base artifact
      const baseSize = statSync(BASE_DIR + '/' + artifactPath).size;
      const baseSizeGzip = gzipSize.fileSync(BASE_DIR + '/' + artifactPath);

      const headSize = statSync(HEAD_DIR + '/' + artifactPath).size;
      const headSizeGzip = gzipSize.fileSync(HEAD_DIR + '/' + artifactPath);
      resultsMap.set(artifactPath, {
        path: artifactPath,
        headSize,
        headSizeGzip,
        baseSize,
        baseSizeGzip,
        change: (headSize - baseSize) / baseSize,
        changeGzip: (headSizeGzip - baseSizeGzip) / baseSizeGzip,
      });
    } catch {
      // There's no matching base artifact. This is a new file.
      const baseSize = 0;
      const baseSizeGzip = 0;
      const headSize = statSync(HEAD_DIR + '/' + artifactPath).size;
      const headSizeGzip = gzipSize.fileSync(HEAD_DIR + '/' + artifactPath);
      resultsMap.set(artifactPath, {
        path: artifactPath,
        headSize,
        headSizeGzip,
        baseSize,
        baseSizeGzip,
        change: Infinity,
        changeGzip: Infinity,
      });
    }
  }

  // Check for base artifacts that were deleted in the head.
  const baseArtifactPaths = await glob('**/*.js', {cwd: 'base-build'});
  for (const artifactPath of baseArtifactPaths) {
    if (!resultsMap.has(artifactPath)) {
      const baseSize = statSync(BASE_DIR + '/' + artifactPath).size;
      const baseSizeGzip = gzipSize.fileSync(BASE_DIR + '/' + artifactPath);
      const headSize = 0;
      const headSizeGzip = 0;
      resultsMap.set(artifactPath, {
        path: artifactPath,
        headSize,
        headSizeGzip,
        baseSize,
        baseSizeGzip,
        change: -1,
        changeGzip: -1,
      });
    }
  }

  const results = Array.from(resultsMap.values());
  results.sort((a, b) => b.change - a.change);

  let criticalResults = [];
  for (const artifactPath of CRITICAL_ARTIFACT_PATHS) {
    const result = resultsMap.get(artifactPath);
    if (result === undefined) {
      throw new Error(
        'Missing expected bundle. If this was an intentional change to the ' +
          'build configuration, update Dangerfile.js accordingly: ' +
          artifactPath
      );
    }
    criticalResults.push(row(result));
  }

  let significantResults = [];
  for (const result of results) {
    // If result exceeds critical threshold, add to top section.
    if (
      (result.change > CRITICAL_THRESHOLD ||
        0 - result.change > CRITICAL_THRESHOLD ||
        // New file
        result.change === Infinity ||
        // Deleted file
        result.change === -1) &&
      // Skip critical artifacts. We added those earlier, in a fixed order.
      !CRITICAL_ARTIFACT_PATHS.has(result.path)
    ) {
      criticalResults.push(row(result));
    }

    // Do the same for results that exceed the significant threshold. These
    // will go into the bottom, collapsed section. Intentionally including
    // critical artifacts in this section, too.
    if (
      result.change > SIGNIFICANCE_THRESHOLD ||
      0 - result.change > SIGNIFICANCE_THRESHOLD ||
      result.change === Infinity ||
      result.change === -1
    ) {
      significantResults.push(row(result));
    }
  }

  markdown(`
Comparing: ${baseSha}...${headSha}

## Critical size changes

Includes critical production bundles, as well as any change greater than ${CRITICAL_THRESHOLD *
    100}%:

${header}
${criticalResults.join('\n')}

## Significant size changes

Includes any change greater than ${SIGNIFICANCE_THRESHOLD * 100}%:

${
  significantResults.length > 0
    ? `

Expand to show
${header}
${significantResults.join('\n')}

`
    : '(No significant changes)'
}
`);
})();
<|endoftext|>


<commit_before>cronenberg.onEnterEditMode(function() {
  $(".ds-dashboard-info-edit-panel").html(ds.templates.edit.info_panel(ds.manager.current.dashboard));
});

$(document).ready(function() {

  /**
   * Handlers for the Info Edit panel.
   */
  $(document).on('click', '#ds-edit-info-button', function(e) {

    if (cronenberg.toggleEditMode().editing) {
      $.fn.editable.defaults.mode = 'inline';

      /** Title */
      $("#ds-info-panel-edit-title").editable({
        unsavedclass: null,
        success: function(ignore, newValue) {
          var dash = ds.manager.current.dashboard.toJSON();
          delete dash.definition;
          dash.title = newValue;
          ds.manager.update(dash);
        }
      });

      /** Category */
      $("#ds-info-panel-edit-category").editable({
        unsavedclass: null,
        success: function(ignore, newValue) {
          var dash = ds.manager.current.dashboard.toJSON();
          delete dash.definition;
          dash.category = newValue;
          ds.manager.update(dash);
        }
      });

      $("#ds-info-panel-edit-summary").editable({
        unsavedclass: null,
        success: function(ignore, newValue) {
          var dash = ds.manager.current.dashboard.toJSON();
          delete dash.definition;
          dash.summary = newValue;
          ds.manager.update(dash);
        }
      });

      $("#ds-info-panel-edit-description").editable({
        unsavedclass: null,
        value: ds.manager.current.dashboard.description || '',
        success: function(ignore, newValue) {
          var dash = ds.manager.current.dashboard.toJSON();
          delete dash.definition;
          dash.description = newValue;
          ds.manager.update(dash);
        },
        display: function(value, response) {
          $(this).html(markdown.toHTML(value));
        }
      });
    }
  });

  $(document).on('click', '#ds-toggle-interactive-button', function(e) {
    if (ds.manager.toggle_interactive_charts()) {
      $('#ds-toggle-interactive-button').removeClass('active');
    } else {
      $('#ds-toggle-interactive-button').addClass('active');
    }
  });

  $(document).on('click', '#ds-enter-fullscreen-button', function(e) {
    cronenberg.enterFullScreen();
  });

  $(document).on('click', '#ds-delete-dashboard-button', function(e) {
    ds.manager.delete_current();
  });

  cronenberg.onEnterEditMode(function() {
    $('#ds-edit-button').addClass('active');
  }).onExitEditMode(function() {
    $('#ds-edit-button').removeClass('active');
  });

});
<commit_msg>Update the model before sending the JSON request<commit_after>cronenberg.onEnterEditMode(function() {
  $(".ds-dashboard-info-edit-panel").html(ds.templates.edit.info_panel(ds.manager.current.dashboard));
});

$(document).ready(function() {

  /**
   * Handlers for the Info Edit panel.
   */
  $(document).on('click', '#ds-edit-info-button', function(e) {

    if (cronenberg.toggleEditMode().editing) {
      $.fn.editable.defaults.mode = 'inline';

      /** Title */
      $("#ds-info-panel-edit-title").editable({
        unsavedclass: null,
        success: function(ignore, newValue) {
          ds.manager.current.dashboard.title = newValue;
          var dash = ds.manager.current.dashboard.toJSON();
          delete dash.definition;
          ds.manager.update(dash);
        }
      });

      /** Category */
      $("#ds-info-panel-edit-category").editable({
        unsavedclass: null,
        success: function(ignore, newValue) {
          ds.manager.current.dashboard.category = newValue;
          var dash = ds.manager.current.dashboard.toJSON();
          delete dash.definition;
          ds.manager.update(dash);
        }
      });

      $("#ds-info-panel-edit-summary").editable({
        unsavedclass: null,
        success: function(ignore, newValue) {
          ds.manager.current.dashboard.summary = newValue;
          var dash = ds.manager.current.dashboard.toJSON();
          delete dash.definition;
          ds.manager.update(dash);
        }
      });

      $("#ds-info-panel-edit-description").editable({
        unsavedclass: null,
        value: ds.manager.current.dashboard.description || '',
        success: function(ignore, newValue) {
          ds.manager.current.dashboard.description = newValue;
          var dash = ds.manager.current.dashboard.toJSON();
          delete dash.definition;
          ds.manager.update(dash);
        },
        display: function(value, response) {
          $(this).html(markdown.toHTML(value));
        }
      });
    }
  });

  $(document).on('click', '#ds-toggle-interactive-button', function(e) {
    if (ds.manager.toggle_interactive_charts()) {
      $('#ds-toggle-interactive-button').removeClass('active');
    } else {
      $('#ds-toggle-interactive-button').addClass('active');
    }
  });

  $(document).on('click', '#ds-enter-fullscreen-button', function(e) {
    cronenberg.enterFullScreen();
  });

  $(document).on('click', '#ds-delete-dashboard-button', function(e) {
    ds.manager.delete_current();
  });

  cronenberg.onEnterEditMode(function() {
    $('#ds-edit-button').addClass('active');
  }).onExitEditMode(function() {
    $('#ds-edit-button').removeClass('active');
  });

});
<|endoftext|>


<commit_before>'use strict';

/*
 * Tanura internal library.
 *
 * This script defines plumbing specific to Tanura, that is not meant for use by
 * the end-user.
 */

(function() {

    /*
     * Create an alias for an event.
     *
     * This will fire a given event whenever another given event is fired.  The
     * return value indicates success.  The callback parameter will be carried
     * through to the new event.
     */
    var aliasEvent = function(alias, event) {
        return this.register(event, function(x) {
            return this.fire(alias, x);
        }, this);
    }.bind(tanura.eventHandler);

    /*
     * Group a list of events into another event.
     *
     * This will fire a given event whenever an event from a given list fires.
     * The return value will be true, if the grouping succeeded, false
     * otherwise. If the grouping fails due to some of the grouped events not
     * existing the rest will still be grouped. The callback parameter will be
     * propagated to the new event.
     */
    var groupEvents = function(_, l) {
        var r = true;
        for (var i of l) { if (! aliasEvent(_, i)) { r = false; } }
        return r;
    }.bind(tanura.eventHandler);

    aliasEvent('media_denied', 'media_denied_fallback');
    groupEvents(
            'media_granted',
            ['media_granted_preferred', 'media_granted_fallback']);
    groupEvents(
            'whiteboard_updated',
            ['whiteboard_edited', 'whiteboard_changed']);

})();

<commit_msg>Fire whiteboard_initialized.<commit_after>'use strict';

/*
 * Tanura internal library.
 *
 * This script defines plumbing specific to Tanura, that is not meant for use by
 * the end-user.
 */

(function() {

    /*
     * Create an alias for an event.
     *
     * This will fire a given event whenever another given event is fired.  The
     * return value indicates success.  The callback parameter will be carried
     * through to the new event.
     */
    var aliasEvent = function(alias, event) {
        return this.register(event, function(x) {
            return this.fire(alias, x);
        }, this);
    }.bind(tanura.eventHandler);

    /*
     * Group a list of events into another event.
     *
     * This will fire a given event whenever an event from a given list fires.
     * The return value will be true, if the grouping succeeded, false
     * otherwise. If the grouping fails due to some of the grouped events not
     * existing the rest will still be grouped. The callback parameter will be
     * propagated to the new event.
     */
    var groupEvents = function(_, l) {
        var r = true;
        for (var i of l) { if (! aliasEvent(_, i)) { r = false; } }
        return r;
    }.bind(tanura.eventHandler);

    aliasEvent('media_denied', 'media_denied_fallback');
    groupEvents(
            'media_granted',
            ['media_granted_preferred', 'media_granted_fallback']);
    groupEvents(
            'whiteboard_updated',
            ['whiteboard_edited', 'whiteboard_changed']);
    groupEvents(
            'whiteboard_initialized',
            ['whiteboard_created', 'whiteboard_loaded']);

})();

<|endoftext|>


<commit_before>import { combineReducers } from 'redux';
import { reducer as authReducer } from './auth';
import { reducer as featureReducer } from './feature';
import { reducer as formReducer } from 'redux-form';

const rootReducer = combineReducers({
    form: formReducer,
    auth: authReducer,
    features: featureReducer
});

export default rootReducer;<commit_msg>Init reducer<commit_after>import { combineReducers } from 'redux';
import { reducer as authReducer } from './auth';
import { reducer as featureReducer } from './feature';
import { reducer as formReducer } from 'redux-form';

const rootReducer = combineReducers({
    form: formReducer,
    auth: authReducer,
    features: featureReducer
});

export default rootReducer;
<|endoftext|>


<commit_before>var React = require('react');
var Nav = require('./navigationView');
var JobsList = require('./jobsView');
var Map = require('./mapView');
var Input = require('./inputView');
var Selections = require('./selectionsView');
var Metrics = require('./metricsView');
var Locs = require('./../collections/locations');
var Jobs = require('./../collections/jobs');
var Titles = require('./../collections/titles');

var AppView = React.createClass({

  getInitialState: function() {
    //create new Backbone collections to hold our data
    return {
      zoomFlag: false,
      zoomoutFlag: false,
      location: '',
      title: '',
      jobs: new Jobs(),
      allLocs: new Locs(),
      filteredLocs: new Locs(),
      titles: new Titles(),
      errorMessage: '',
      showResults: false
    }
  },

  componentDidMount: function() {
    var context = this;
    this.state.filteredLocs.fetch({
      success: function(locs) {
        context.state.filteredLocs = locs;
        context.refs.map.setMarkers();
      }
    });

    this.state.allLocs.fetch({
      success: function(locs) {
        context.state.titles.fetch({
          success: function(titles) {
            context.state.allLocs = locs;
            context.state.titles = titles;
            context.refs.nav.autoFill();
          }
        });
      }
    });
  },

  updateSearch: function(location, title, zoomFlag, zoomoutFlag) {
    //no location or title passed in response - reset everything
    if(!location && !title) {
      if(this.state.title) {
        this.locationUpdate(location, title);
      }
      this.clearJobs();
      this.refs.map.resetMap();
    } else {
      this.jobsUpdate(location, title, zoomFlag, zoomoutFlag, false);
    }

  },

  updateClick: function(location) {
    //always update jobs WITH EXISTING TITLE SEARCH!
    this.jobsUpdate(location, this.state.title, false, false, true);
    if (!this.state.showResults) {
      this.refs.map.shrinkMapWithoutZoom();
      this.state.showResults = true;
    }
  },

  clearJobs: function() {
    this.setState({
      jobs: new Jobs(),
      location: '',
      title: '',
      showResults: false
    });
  },

  jobsUpdate: function(location, title, zoomFlag, zoomoutFlag, clickFlag) {
    var context = this;
    var request = {};
    request.location = location || '';
    request.title = title || '';
    this.state.jobs.fetch({
      traditional: true,
      data: request,
      success: function(jobs) {
        if(jobs.length) {
          context.handleZoom(location, title, clickFlag);
          context.setState({
            jobs: jobs,
            location: request.location,
            title: request.title,
            zoomFlag: zoomFlag,
            zoomoutFlag: zoomoutFlag,
            // filteredLocs: context.state.filteredLocs,
            errorMessage: ''
          });
        } else {
          context.setState({
            zoomFlag: false,
            zoomoutFlag: false,
            errorMessage: 'No jobs found, try another search.'
          });
        }
      }
    });
  },

  handleZoom: function(location, title, clickFlag) {
    if(!clickFlag && (title || this.state.title))  {
      this.locationUpdate(location, title);
    }
    if (!this.state.showResults) {
      if(!location) {
        this.refs.map.shrinkMapWithoutZoom();
      } else {
        this.refs.map.shrinkMapWithZoom();
      }
      this.setState({
        showResults: true
      });
    }
    //if user is searching a new title in THIS REQUEST
    //OR if user search for a title LAST REQUEST and we need to clear it
  },

  locationUpdate: function(location, title) {
    var context = this;
    this.state.filteredLocs.fetch({
      traditional: true,
      data: {title: title},
      success: function(newLocs) {
        context.setState({
          filteredLocs: newLocs
        });
        // context.state.filteredLocs = newLocs;
        context.refs.map.setMarkers();
      }
    });
  },

  render: function() {
    if (this.state.showResults) {
      return (

          <Map updateClick={this.updateClick} locs={this.state.filteredLocs} location={this.state.location} zoomFlag={this.state.zoomFlag} zoomoutFlag={this.state.zoomoutFlag} ref="map" />
          <Selections updateSearch={this.updateSearch} location={this.state.location} title={this.state.title} />
          <Metrics jobs={this.state.jobs} location={this.state.location} locs={this.state.filteredLocs} />
          <JobsList jobs={this.state.jobs} location={this.state.location} title={this.state.title} />

      );
    } else {
      return (

          <Map updateClick={this.updateClick} locs={this.state.filteredLocs} location={this.state.location} zoomFlag={this.state.zoomFlag} ref="map" />

      );
    }
  }

});

module.exports = function() {
  React.render(<AppView />, document.getElementById('main'));
};

<commit_msg>remove unnecessary setState call<commit_after>var React = require('react');
var Nav = require('./navigationView');
var JobsList = require('./jobsView');
var Map = require('./mapView');
var Input = require('./inputView');
var Selections = require('./selectionsView');
var Metrics = require('./metricsView');
var Locs = require('./../collections/locations');
var Jobs = require('./../collections/jobs');
var Titles = require('./../collections/titles');

var AppView = React.createClass({

  getInitialState: function() {
    //create new Backbone collections to hold our data
    return {
      zoomFlag: false,
      zoomoutFlag: false,
      location: '',
      title: '',
      jobs: new Jobs(),
      allLocs: new Locs(),
      filteredLocs: new Locs(),
      titles: new Titles(),
      errorMessage: '',
      showResults: false
    }
  },

  componentDidMount: function() {
    var context = this;
    this.state.filteredLocs.fetch({
      success: function(locs) {
        context.state.filteredLocs = locs;
        context.refs.map.setMarkers();
      }
    });

    this.state.allLocs.fetch({
      success: function(locs) {
        context.state.titles.fetch({
          success: function(titles) {
            context.state.allLocs = locs;
            context.state.titles = titles;
            context.refs.nav.autoFill();
          }
        });
      }
    });
  },

  updateSearch: function(location, title, zoomFlag, zoomoutFlag) {
    //no location or title passed in response - reset everything
    if(!location && !title) {
      if(this.state.title) {
        this.locationUpdate(location, title);
      }
      this.clearJobs();
      this.refs.map.resetMap();
    } else {
      this.jobsUpdate(location, title, zoomFlag, zoomoutFlag, false);
    }

  },

  updateClick: function(location) {
    //always update jobs WITH EXISTING TITLE SEARCH!
    this.jobsUpdate(location, this.state.title, false, false, true);
    if (!this.state.showResults) {
      this.refs.map.shrinkMapWithoutZoom();
      this.state.showResults = true;
    }
  },

  clearJobs: function() {
    this.setState({
      jobs: new Jobs(),
      location: '',
      title: '',
      showResults: false
    });
  },

  jobsUpdate: function(location, title, zoomFlag, zoomoutFlag, clickFlag) {
    var context = this;
    var request = {};
    request.location = location || '';
    request.title = title || '';
    this.state.jobs.fetch({
      traditional: true,
      data: request,
      success: function(jobs) {
        if(jobs.length) {
          context.handleZoom(location, title, clickFlag);
          context.setState({
            jobs: jobs,
            location: request.location,
            title: request.title,
            zoomFlag: zoomFlag,
            zoomoutFlag: zoomoutFlag,
            // filteredLocs: context.state.filteredLocs,
            errorMessage: ''
          });
        } else {
          context.setState({
            zoomFlag: false,
            zoomoutFlag: false,
            errorMessage: 'No jobs found, try another search.'
          });
        }
      }
    });
  },

  handleZoom: function(location, title, clickFlag) {
    if(!clickFlag && (title || this.state.title))  {
      this.locationUpdate(location, title);
    }
    if (!this.state.showResults) {
      if(!location) {
        this.refs.map.shrinkMapWithoutZoom();
      } else {
        this.refs.map.shrinkMapWithZoom();
      }
      this.setState({
        showResults: true
      });
    }
    //if user is searching a new title in THIS REQUEST
    //OR if user search for a title LAST REQUEST and we need to clear it
  },

  locationUpdate: function(location, title) {
    var context = this;
    this.state.filteredLocs.fetch({
      traditional: true,
      data: {title: title},
      success: function(newLocs) {
        context.state.filteredLocs = newLocs;
        context.refs.map.setMarkers();
      }
    });
  },

  render: function() {
    if (this.state.showResults) {
      return (

          <Map updateClick={this.updateClick} locs={this.state.filteredLocs} location={this.state.location} zoomFlag={this.state.zoomFlag} zoomoutFlag={this.state.zoomoutFlag} ref="map" />
          <Selections updateSearch={this.updateSearch} location={this.state.location} title={this.state.title} />
          <Metrics jobs={this.state.jobs} location={this.state.location} locs={this.state.filteredLocs} />
          <JobsList jobs={this.state.jobs} location={this.state.location} title={this.state.title} />

      );
    } else {
      return (

          <Map updateClick={this.updateClick} locs={this.state.filteredLocs} location={this.state.location} zoomFlag={this.state.zoomFlag} ref="map" />

      );
    }
  }

});

module.exports = function() {
  React.render(<AppView />, document.getElementById('main'));
};

<|endoftext|>


<commit_before>/* global danger, fail, warn, markdown, message */
'use strict';
// requires
const fs = require('fs-extra');
const path = require('path');
const DOMParser = require('xmldom').DOMParser;
// Due to bug in danger, we hack env variables in build process.
const ENV = fs.existsSync('./env.json') ? require('./env.json') : process.env;
// constants
const JIRARegexp = /https:\/\/jira\.appcelerator\.org\/browse\/[A-Z]+-\d+/;
const github = danger.github;
// Currently used PR-labels
const Label = {
	NEEDS_JIRA: 'needs jira 🚨',
	NEEDS_TESTS: 'needs tests 🚨',
	NEEDS_CLA: 'needs cla 🚨',
	NO_TESTS: 'no tests',
	IOS: 'ios',
	ANDROID: 'android',
	COMMUNITY: 'community 🔥',
	DOCS: 'docs 📔'
};
// Array to gather up the labels we want to auto-apply to the PR
const labels = new Set();

// To spit out the raw data we can use:
// markdown(JSON.stringify(danger));

// Check if the user deleted more code than added, give a thumbs-up if so
if (github.pr.deletions > github.pr.additions) {
	message(':thumbsup: Hey!, You deleted more code than you added. That\'s awesome!');
}

// Check npm test output
if (fs.existsSync('./npm_test.log')) {
	const npmTestOutput = fs.readFileSync('./npm_test.log');
	if (npmTestOutput.indexOf('Test failed.  See above for more details.') !== -1) {
		fail(':disappointed_relieved: `npm test` failed. See below for details.');
		message('```' + npmTestOutput + '\n```');
	}
}

// TODO Check for PRs above a certain threshold of changes and warn?

// Check that we have a JIRA Link in the body
const body = github.pr.body;
const hasJIRALink = body.match(JIRARegexp);
if (!hasJIRALink) {
	labels.add(Label.NEEDS_JIRA);
	warn('There is no linked JIRA ticket in the PR body. Please include the URL of the relevant JIRA ticket. If you need to, you may file a ticket on ' + danger.utils.href('https://jira.appcelerator.org/secure/CreateIssue!default.jspa', 'JIRA'));
} else {
	// If it has the "needs jira" label, remove it since we do have one linked
	const hasNeedsJIRALabel = github.issue.labels.some(function (label) {
		return label.name === Label.NEEDS_JIRA;
	});
	if (hasNeedsJIRALabel) {
		github.api.issues.removeLabel({ owner: github.pr.base.repo.owner.login, repo: github.pr.base.repo.name, number: github.pr.number, name: Label.NEEDS_JIRA });
	}
}

// Check that package.json and package-lock.json stay in-sync
const hasPackageChanges = danger.git.modified_files.indexOf('package.json') !== -1;
const hasLockfileChanges = danger.git.modified_files.indexOf('package-lock.json') !== -1;
if (hasPackageChanges && !hasLockfileChanges) {
	warn(':lock: Changes were made to package.json, but not to package-lock.json - Perhaps you need to run `npm install`?');
}

// Check that if we modify the Android or iOS SDK, we also update the tests
const modified = danger.git.modified_files.concat(danger.git.created_files);
const modifiedAndroidFiles = modified.filter(function (p) {
	return p.startsWith('android/') && p.endsWith('.java');
});
const modifiedIOSFiles = modified.filter(function (p) {
	return p.startsWith('iphone/Classes/') && (p.endsWith('.h') || p.endsWith('.m'));
});

// Auto-assign android/ios labels
if (modifiedAndroidFiles.length > 0) {
	labels.add(Label.ANDROID);
}
if (modifiedIOSFiles.length > 0) {
	labels.add(Label.IOS);
}
// Check if apidoc was modified and apply 'docs' label?
const modifiedApiDocs = modified.filter(function (p) {
	return p.startsWith('apidoc/');
});
if (modifiedApiDocs.length > 0) {
	labels.push(Label.DOCS);
}

// Check PR author to see if it's community, etc
if (github.pr.author_association === 'FIRST_TIMER') {
	labels.add(Label.COMMUNITY);
	labels.add(Label.NEEDS_CLA);
	// Thank them profusely! This is their first ever github commit!
	message(`:rocket: Wow, ${github.pr.user.login}, your first contribution to GitHub and it's to help us make Titanium better! You rock! :guitar:`);
} else if (github.pr.author_association === 'FIRST_TIME_CONTRIBUTOR') {
	labels.add(Label.COMMUNITY);
	labels.add(Label.NEEDS_CLA);
	// Thank them, this is their first contribution to this repo!
	message(`:confetti_ball: Welcome to the Titanium SDK community, ${github.pr.user.login}! Thank you so much for your PR, you're helping us make Titanium better. :gift:`);
} else if (github.pr.author_association === 'CONTRIBUTOR') {
	labels.add(Label.COMMUNITY);
	// Be nice, this is a community member who has landed PRs before!
	message(`:tada: Another contribution from our awesome community member, ${github.pr.user.login}! Thanks again for helping us make Titanium SDK better. :thumbsup:`);
}

// Check if any tests were changed/added
const hasAppChanges = (modifiedAndroidFiles.length + modifiedIOSFiles.length) > 0;
const testChanges = modified.filter(function (p) {
	return p.startsWith('tests/') && p.endsWith('.js');
});
const hasTestChanges = testChanges.length > 0;
const hasNoTestsLabel = github.issue.labels.some(function (label) {
	return label.name === Label.NO_TESTS;
});
// If we changed android/iOS source, but didn't change tests and didn't use the 'no tests' label
// fail the PR
if (hasAppChanges && !hasTestChanges && !hasNoTestsLabel) {
	labels.add(Label.NEEDS_TESTS);
	const testDocLink = github.utils.fileLinks([ 'README.md#unit-tests' ]);
	fail(`:microscope: There are library changes, but no changes to the unit tests. That's OK as long as you're refactoring existing code, but will require an admin to merge this PR. Please see ${testDocLink} for docs on unit testing.`); // eslint-disable-line max-len
} else {
	// If it has the "needs tests" label, remove it
	const hasNeedsTestsLabel = github.issue.labels.some(function (label) {
		return label.name === Label.NEEDS_TESTS;
	});
	if (hasNeedsTestsLabel) {
		github.api.issues.removeLabel({ owner: github.pr.base.repo.owner.login, repo: github.pr.base.repo.name, number: github.pr.number, name: Label.NEEDS_TESTS });
	}
}

// Now apply our labels
github.api.issues.addLabels({ owner: github.pr.base.repo.owner.login, repo: github.pr.base.repo.name, number: github.pr.number, labels: labels });

// Check for iOS crash file
const crashFiles = fs.readdirSync(__dirname).filter(function (p) {
	return p.startsWith('mocha_') && p.endsWith('.crash');
});
if (crashFiles.length > 0) {
	const crashLink = danger.utils.href(`${ENV.BUILD_URL}artifact/${crashFiles[0]}`, 'the crash log');
	fail(`Test suite crashed on iOS simulator. Please see ${crashLink} for more details.`);
}

// Report test failures
function gatherFailedTestcases(reportPath) {
	if (!fs.existsSync(reportPath)) {
		return [];
	}
	const contents = fs.readFileSync(reportPath);
	const doc = new DOMParser().parseFromString(contents.toString(), 'text/xml');
	const suite_root = doc.documentElement.firstChild.tagName === 'testsuites' ? doc.documentElement.firstChild : doc.documentElement;
	const suites = Array.from(suite_root.getElementsByTagName('testsuite'));

	// We need to get the 'testcase' elements that have an 'error' or 'failure' child node
	const failed_suites = suites.filter(function (suite) {
		const hasFailures = suite.hasAttribute('failures') && parseInt(suite.getAttribute('failures')) !== 0;
		const hasErrors = suite.hasAttribute('errors') && parseInt(suite.getAttribute('errors')) !== 0;
		return hasFailures || hasErrors;
	});
	// Gather all the testcase nodes from each failed suite properly.
	let failed_suites_all_tests = [];
	failed_suites.forEach(function (suite) {
		failed_suites_all_tests = failed_suites_all_tests.concat(Array.from(suite.getElementsByTagName('testcase')));
	});
	return failed_suites_all_tests.filter(function (test) {
		return test.hasChildNodes() && (test.getElementsByTagName('failure').length > 0 || test.getElementsByTagName('error').length > 0);
	});
}

// Give details on failed mocha suite tests
const failedAndroidTests = gatherFailedTestcases(path.join(__dirname, 'junit.android.xml'));
const failedIOSTests = gatherFailedTestcases(path.join(__dirname, 'junit.ios.xml'));
const failures_and_errors = [ ...failedAndroidTests, ...failedIOSTests ];
if (failures_and_errors.length !== 0) {
	fail('Tests have failed, see below for more information.');
	let message = '### Tests: \n\n';
	const keys = Array.from(failures_and_errors[0].attributes).map(function (attr) {
		return attr.nodeName;
	});
	const attributes = keys.map(function (key) {
		return key.substr(0, 1).toUpperCase() + key.substr(1).toLowerCase();
	});
	attributes.push('Error');

	// TODO Include stderr/stdout too?
	// Create the headers
	message += '| ' + attributes.join(' | ') + ' |\n';
	message += '| ' + attributes.map(function () {
		return '---';
	}).join(' | ') + ' |\n';

	// Map out the keys to the tests
	failures_and_errors.forEach(function (test) {
		const row_values = keys.map(function (key) {
			return test.getAttribute(key);
		});
		// push error/failure message too
		const errors = test.getElementsByTagName('error');
		if (errors.length !== 0) {
			row_values.push(errors.item(0).getAttribute('message') + errors.item(0).getAttribute('stack'));
		} else {
			const failures = test.getElementsByTagName('failure');
			if (failures.length !== 0) {
				row_values.push(failures.item(0).getAttribute('message') + failures.item(0).getAttribute('stack'));
			} else {
				row_values.push(''); // This shouldn't ever happen
			}
		}
		message += '| ' + row_values.join(' | ') + ' |\n';
	});

	markdown(message);
}

// Add link to built SDK zipfile!
if (ENV.BUILD_STATUS === 'SUCCESS' || ENV.BUILD_STATUS === 'UNSTABLE') {
	const sdkLink = danger.utils.href(`${ENV.BUILD_URL}artifact/${ENV.ZIPFILE}`, 'Here\'s the generated SDK zipfile');
	message(`:floppy_disk: ${sdkLink}.`);
}

// TODO Pass along any warnings/errors from eslint in a readable way? Right now we don't have any way to get at the output of the eslint step of npm test
// May need to edit Jenkinsfile to do a try/catch to spit out the npm test output to some file this dangerfile can consume?
// Or port https://github.com/leonhartX/danger-eslint/blob/master/lib/eslint/plugin.rb to JS - have it run on any edited/added JS files?
<commit_msg>Update dangerfile.js (#9953)<commit_after>/* global danger, fail, warn, markdown, message */
'use strict';
// requires
const fs = require('fs-extra');
const path = require('path');
const DOMParser = require('xmldom').DOMParser;
// Due to bug in danger, we hack env variables in build process.
const ENV = fs.existsSync('./env.json') ? require('./env.json') : process.env;
// constants
const JIRARegexp = /https:\/\/jira\.appcelerator\.org\/browse\/[A-Z]+-\d+/;
const github = danger.github;
// Currently used PR-labels
const Label = {
	NEEDS_JIRA: 'needs jira 🚨',
	NEEDS_TESTS: 'needs tests 🚨',
	NEEDS_CLA: 'needs cla 🚨',
	NO_TESTS: 'no tests',
	IOS: 'ios',
	ANDROID: 'android',
	COMMUNITY: 'community 🔥',
	DOCS: 'docs 📔'
};
// Array to gather up the labels we want to auto-apply to the PR
const labels = new Set();

// To spit out the raw data we can use:
// markdown(JSON.stringify(danger));

// Check if the user deleted more code than added, give a thumbs-up if so
if (github.pr.deletions > github.pr.additions) {
	message(':thumbsup: Hey!, You deleted more code than you added. That\'s awesome!');
}

// Check npm test output
if (fs.existsSync('./npm_test.log')) {
	const npmTestOutput = fs.readFileSync('./npm_test.log');
	if (npmTestOutput.indexOf('Test failed.  See above for more details.') !== -1) {
		fail(':disappointed_relieved: `npm test` failed. See below for details.');
		message('```' + npmTestOutput + '\n```');
	}
}

// TODO Check for PRs above a certain threshold of changes and warn?

// Check that we have a JIRA Link in the body
const body = github.pr.body;
const hasJIRALink = body.match(JIRARegexp);
if (!hasJIRALink) {
	labels.add(Label.NEEDS_JIRA);
	warn('There is no linked JIRA ticket in the PR body. Please include the URL of the relevant JIRA ticket. If you need to, you may file a ticket on ' + danger.utils.href('https://jira.appcelerator.org/secure/CreateIssue!default.jspa', 'JIRA'));
} else {
	// If it has the "needs jira" label, remove it since we do have one linked
	const hasNeedsJIRALabel = github.issue.labels.some(function (label) {
		return label.name === Label.NEEDS_JIRA;
	});
	if (hasNeedsJIRALabel) {
		github.api.issues.removeLabel({ owner: github.pr.base.repo.owner.login, repo: github.pr.base.repo.name, number: github.pr.number, name: Label.NEEDS_JIRA });
	}
}

// Check that package.json and package-lock.json stay in-sync
const hasPackageChanges = danger.git.modified_files.indexOf('package.json') !== -1;
const hasLockfileChanges = danger.git.modified_files.indexOf('package-lock.json') !== -1;
if (hasPackageChanges && !hasLockfileChanges) {
	warn(':lock: Changes were made to package.json, but not to package-lock.json - Perhaps you need to run `npm install`?');
}

// Check that if we modify the Android or iOS SDK, we also update the tests
const modified = danger.git.modified_files.concat(danger.git.created_files);
const modifiedAndroidFiles = modified.filter(function (p) {
	return p.startsWith('android/') && p.endsWith('.java');
});
const modifiedIOSFiles = modified.filter(function (p) {
	return p.startsWith('iphone/Classes/') && (p.endsWith('.h') || p.endsWith('.m'));
});

// Auto-assign android/ios labels
if (modifiedAndroidFiles.length > 0) {
	labels.add(Label.ANDROID);
}
if (modifiedIOSFiles.length > 0) {
	labels.add(Label.IOS);
}
// Check if apidoc was modified and apply 'docs' label?
const modifiedApiDocs = modified.filter(function (p) {
	return p.startsWith('apidoc/');
});
if (modifiedApiDocs.length > 0) {
	labels.add(Label.DOCS);
}

// Check PR author to see if it's community, etc
if (github.pr.author_association === 'FIRST_TIMER') {
	labels.add(Label.COMMUNITY);
	labels.add(Label.NEEDS_CLA);
	// Thank them profusely! This is their first ever github commit!
	message(`:rocket: Wow, ${github.pr.user.login}, your first contribution to GitHub and it's to help us make Titanium better! You rock! :guitar:`);
} else if (github.pr.author_association === 'FIRST_TIME_CONTRIBUTOR') {
	labels.add(Label.COMMUNITY);
	labels.add(Label.NEEDS_CLA);
	// Thank them, this is their first contribution to this repo!
	message(`:confetti_ball: Welcome to the Titanium SDK community, ${github.pr.user.login}! Thank you so much for your PR, you're helping us make Titanium better. :gift:`);
} else if (github.pr.author_association === 'CONTRIBUTOR') {
	labels.add(Label.COMMUNITY);
	// Be nice, this is a community member who has landed PRs before!
	message(`:tada: Another contribution from our awesome community member, ${github.pr.user.login}! Thanks again for helping us make Titanium SDK better. :thumbsup:`);
}

// Check if any tests were changed/added
const hasAppChanges = (modifiedAndroidFiles.length + modifiedIOSFiles.length) > 0;
const testChanges = modified.filter(function (p) {
	return p.startsWith('tests/') && p.endsWith('.js');
});
const hasTestChanges = testChanges.length > 0;
const hasNoTestsLabel = github.issue.labels.some(function (label) {
	return label.name === Label.NO_TESTS;
});
// If we changed android/iOS source, but didn't change tests and didn't use the 'no tests' label
// fail the PR
if (hasAppChanges && !hasTestChanges && !hasNoTestsLabel) {
	labels.add(Label.NEEDS_TESTS);
	const testDocLink = github.utils.fileLinks([ 'README.md#unit-tests' ]);
	fail(`:microscope: There are library changes, but no changes to the unit tests. That's OK as long as you're refactoring existing code, but will require an admin to merge this PR. Please see ${testDocLink} for docs on unit testing.`); // eslint-disable-line max-len
} else {
	// If it has the "needs tests" label, remove it
	const hasNeedsTestsLabel = github.issue.labels.some(function (label) {
		return label.name === Label.NEEDS_TESTS;
	});
	if (hasNeedsTestsLabel) {
		github.api.issues.removeLabel({ owner: github.pr.base.repo.owner.login, repo: github.pr.base.repo.name, number: github.pr.number, name: Label.NEEDS_TESTS });
	}
}

// Now apply our labels
github.api.issues.addLabels({ owner: github.pr.base.repo.owner.login, repo: github.pr.base.repo.name, number: github.pr.number, labels: labels });

// Check for iOS crash file
const crashFiles = fs.readdirSync(__dirname).filter(function (p) {
	return p.startsWith('mocha_') && p.endsWith('.crash');
});
if (crashFiles.length > 0) {
	const crashLink = danger.utils.href(`${ENV.BUILD_URL}artifact/${crashFiles[0]}`, 'the crash log');
	fail(`Test suite crashed on iOS simulator. Please see ${crashLink} for more details.`);
}

// Report test failures
function gatherFailedTestcases(reportPath) {
	if (!fs.existsSync(reportPath)) {
		return [];
	}
	const contents = fs.readFileSync(reportPath);
	const doc = new DOMParser().parseFromString(contents.toString(), 'text/xml');
	const suite_root = doc.documentElement.firstChild.tagName === 'testsuites' ? doc.documentElement.firstChild : doc.documentElement;
	const suites = Array.from(suite_root.getElementsByTagName('testsuite'));

	// We need to get the 'testcase' elements that have an 'error' or 'failure' child node
	const failed_suites = suites.filter(function (suite) {
		const hasFailures = suite.hasAttribute('failures') && parseInt(suite.getAttribute('failures')) !== 0;
		const hasErrors = suite.hasAttribute('errors') && parseInt(suite.getAttribute('errors')) !== 0;
		return hasFailures || hasErrors;
	});
	// Gather all the testcase nodes from each failed suite properly.
	let failed_suites_all_tests = [];
	failed_suites.forEach(function (suite) {
		failed_suites_all_tests = failed_suites_all_tests.concat(Array.from(suite.getElementsByTagName('testcase')));
	});
	return failed_suites_all_tests.filter(function (test) {
		return test.hasChildNodes() && (test.getElementsByTagName('failure').length > 0 || test.getElementsByTagName('error').length > 0);
	});
}

// Give details on failed mocha suite tests
const failedAndroidTests = gatherFailedTestcases(path.join(__dirname, 'junit.android.xml'));
const failedIOSTests = gatherFailedTestcases(path.join(__dirname, 'junit.ios.xml'));
const failures_and_errors = [ ...failedAndroidTests, ...failedIOSTests ];
if (failures_and_errors.length !== 0) {
	fail('Tests have failed, see below for more information.');
	let message = '### Tests: \n\n';
	const keys = Array.from(failures_and_errors[0].attributes).map(function (attr) {
		return attr.nodeName;
	});
	const attributes = keys.map(function (key) {
		return key.substr(0, 1).toUpperCase() + key.substr(1).toLowerCase();
	});
	attributes.push('Error');

	// TODO Include stderr/stdout too?
	// Create the headers
	message += '| ' + attributes.join(' | ') + ' |\n';
	message += '| ' + attributes.map(function () {
		return '---';
	}).join(' | ') + ' |\n';

	// Map out the keys to the tests
	failures_and_errors.forEach(function (test) {
		const row_values = keys.map(function (key) {
			return test.getAttribute(key);
		});
		// push error/failure message too
		const errors = test.getElementsByTagName('error');
		if (errors.length !== 0) {
			row_values.push(errors.item(0).getAttribute('message') + errors.item(0).getAttribute('stack'));
		} else {
			const failures = test.getElementsByTagName('failure');
			if (failures.length !== 0) {
				row_values.push(failures.item(0).getAttribute('message') + failures.item(0).getAttribute('stack'));
			} else {
				row_values.push(''); // This shouldn't ever happen
			}
		}
		message += '| ' + row_values.join(' | ') + ' |\n';
	});

	markdown(message);
}

// Add link to built SDK zipfile!
if (ENV.BUILD_STATUS === 'SUCCESS' || ENV.BUILD_STATUS === 'UNSTABLE') {
	const sdkLink = danger.utils.href(`${ENV.BUILD_URL}artifact/${ENV.ZIPFILE}`, 'Here\'s the generated SDK zipfile');
	message(`:floppy_disk: ${sdkLink}.`);
}

// TODO Pass along any warnings/errors from eslint in a readable way? Right now we don't have any way to get at the output of the eslint step of npm test
// May need to edit Jenkinsfile to do a try/catch to spit out the npm test output to some file this dangerfile can consume?
// Or port https://github.com/leonhartX/danger-eslint/blob/master/lib/eslint/plugin.rb to JS - have it run on any edited/added JS files?
<|endoftext|>


<commit_before>import React, {Component} from 'react'
import pureRender from 'pure-render-decorator'

import '../../css/about-modal-content.less'

@pureRender
export default class AboutModalContent extends Component {
  render() {
    return (

About: Seismic Explorer

Geologists collect earthquake data everyday. What are the patterns of earthquake magnitude, depth,
            location, and frequency? What are the patterns of Earthquakes along plate boundaries?

Click the play button to see the earthquakes. You can drag the starting time to start playing earthquakes
            from a later date.

Use the Magnitude slider to choose the earthquake size shown on the map. Click the Show plate boundaries
            button to see the outlines of tectonic plates.

Make a cross-section to see a three-dimensional view of the earthquakes in a region. Click on the Draw a
            cross-section line then draw a line on the map. When you are done, click Open 3D view, and see the depths of
            the earthquakes in that cross-section. What does the pattern of earthquakes in a region tell you about the
            motion of tectonic plates?

Seismic Explorer is based on Seismic Eruption, a program created by Alan L. Jones at the State University
            of New York at Binghamton.

Seismic Explorer uses earthquake data (magnitude, depth, location, time) from the United States Geological Survey.

    )
  }
}

<commit_msg>Update About text<commit_after>import React, {Component} from 'react'
import pureRender from 'pure-render-decorator'

import '../../css/about-modal-content.less'

@pureRender
export default class AboutModalContent extends Component {
  render() {
    return (

About: Seismic Explorer

Geologists collect earthquake data everyday. What are the patterns of earthquake magnitude, depth,
            location, and frequency? What are the patterns of earthquakes along plate boundaries?

Click the play button to see the earthquakes. You can drag the starting time to start playing earthquakes
            from a later date.

Use the Magnitude slider to choose the earthquake size shown on the map. Click the Show plate boundaries
            button to see the outlines of tectonic plates.

Make a cross-section to see a three-dimensional view of the earthquakes in a region. Click on the Draw a
            cross-section line then draw a line on the map. When you are done, click Open 3D view, and see the depths of
            the earthquakes in that cross-section. What does the pattern of earthquakes in a region tell you about the
            motion of tectonic plates?

Seismic Explorer is based on Seismic Eruption, a program created by Alan L. Jones at the State University
            of New York at Binghamton.

Seismic Explorer uses earthquake data (magnitude, depth, location, time) from the United States Geological Survey.

    )
  }
}

<|endoftext|>


<commit_before>angular.module( 'App.Views' ).config( function( $stateProvider )
{
	$stateProvider.state( 'forums.topics.view', {
		url: '/f/:topicSlug/:topicId',
		controller: 'Forums.TopicCtrl',
		controllerAs: 'topicCtrl',
		templateUrl: '/app/views/forums/topics/view/view.html',
	} );
} );<commit_msg>Fix forum topic controller name<commit_after>angular.module( 'App.Views' ).config( function( $stateProvider )
{
	$stateProvider.state( 'forums.topics.view', {
		url: '/f/:topicSlug/:topicId',
		controller: 'Forums.Topics.ViewCtrl',
		controllerAs: 'viewCtrl',
		templateUrl: '/app/views/forums/topics/view/view.html',
	} );
} );<|endoftext|>


<commit_before>/* global mapcluster */
/// <reference path="../../typings/angularjs/angular.d.ts""/>
/// <reference path="../../typings/ol3/ol.d.ts""/>
var MERC = 40075016.68;
var HALF_MERC = 20037508.34;
var PI = 245850922 / 78256779;
var MAX_ZOOM = 20;
var MAX_RESOLUTION = 156543.033906;

function createStyle(uri, size) {
    var img = new ol.style.Icon({
        src: '/images/' + uri,
        size: [size,size],
        offset: [0,0],
        offsetOrigin: 'top-left'
    });
    var style = new ol.style.Style({
        image: img
    });
    return style;
}

function drawGrid(zoom, extent, size) {
    size = size || 40;
    var level = zoom;
    var resolution = MAX_RESOLUTION / Math.pow(2, level);
    var gridLen;
    do {
        gridLen = HALF_MERC / Math.pow(2, level);
        var imgLen = size * resolution;
        var outer = gridLen * 2 / 3;
        var inner = gridLen * 1 / 3;
        if (imgLen > outer)
            --level;
        if (imgLen < inner)
            ++level;
    } while (imgLen > outer || imgLen < inner)
    var xmin = extent[0] - gridLen;
    var xmax = extent[2] + gridLen;
    var ymin = extent[1] - gridLen;
    var ymax = extent[3] + gridLen;
    var lines = new Array();
    for (var i = xmin; i <=xmax; i += gridLen) {
        var line = new ol.geom.LineString([[i,ymin], [i,ymax]]);
        lines.push(new ol.Feature(line));
    }
    for (var i = ymin; i <=ymax; i += gridLen) {
        var line = new ol.geom.LineString([[xmin,i], [xmax,i]]);
        lines.push(new ol.Feature(line));
    }
    return lines;
}

var map = new ol.Map({
    view: new ol.View({
        center: [0, 0],
        zoom: 3,
        maxZoom: 20,
        minZoom: 2
    }),
    layers: [
        new ol.layer.Tile({
            source: new ol.source.OSM(),
            title: 'Base OSM'
        })
    ],
    target: 'map'
});
map.addControl(new ol.control.ZoomSlider);
map.addControl(new ol.control.LayerSwitcher);

var gridLayer = new ol.layer.Vector({
    title: 'Grid Layer'
});
map.addLayer(gridLayer);
var fill = new ol.style.Fill({
    color: [255,255,255,0.4]
});
var stroke = new ol.style.Stroke({
    color: [0,150,255,1],
    width: 1.0
});
var originLayer = new ol.layer.Vector({
    title: 'Origin Layer',
    style: new ol.style.Style({
        fill: fill,
        stroke: stroke,
        image: new ol.style.Circle({
            radius: 3,
            fill: fill,
            stroke: stroke
        })
    })
});
map.addLayer(originLayer);
var smallLayer = new ol.layer.Vector({
    title: 'Small Layer'
});
map.addLayer(smallLayer);
var bigLayer = new ol.layer.Vector({
    title: 'Big Layer'
});
map.addLayer(bigLayer);

var app = angular.module('myApp', []);
app.controller('mapCtrl', function($scope, $http) {
    // uncomment this when you import geojson files into mongodb
    // and need to initialize feature properties, including mortonCode
    /* $http.get('http://localhost:3000/features/all').success(function(res) {
        res.forEach(function(fj) {
            var url = 'http://localhost:3000/features/update/' + fj.id;
            $http.post(url, {feature: fj});
        });
    }); */
    function redraw() {
        var imgSize = $scope.imgSize || 40;
        var dScore = $scope.dScore || 9;
        var mapExtent = map.getView().calculateExtent(map.getSize());
        var zoom = map.getView().getZoom();
        var gridLen = HALF_MERC / Math.pow(2, zoom + 1);
        var promise = $http({url: 'http://localhost:3000/features',
                             method: 'GET',
                             params: {xmin: mapExtent[0] - gridLen,
                                      ymin: mapExtent[1] - gridLen,
                                      xmax: mapExtent[2] + gridLen,
                                      ymax: mapExtent[3] + gridLen}});
        promise.success(function(JSONs) {
            var originSource = new ol.source.Vector();
            var originFeatures = new Array();
            JSONs.forEach(function(fj) {
                var originFeature = new ol.format.GeoJSON().readFeature(fj, {
                    dataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:900913'
                });
                originFeatures.push(originFeature);
            });
            originSource.addFeatures(originFeatures);
            originLayer.setSource(originSource);
            $scope.imgCount = originFeatures.length;

            var d = new Date();
            var start = d.getTime();
            var bigJSONs = mapcluster(JSONs, mapExtent, zoom, imgSize, dScore);
            var d = new Date();
            var end = d.getTime();
            var bigFeatures = new Array();
            bigJSONs.forEach(function(bj) {
                var bigFeature = new ol.format.GeoJSON().readFeature(bj, {
                    dataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:900913'
                });
                bigFeature.setStyle(createStyle(bj.properties.imgUrl, imgSize));
                bigFeatures.push(bigFeature);
            });
            var bigSource = new ol.source.Vector({
                projection: 'EPSG:900913'
            });
            bigSource.addFeatures(bigFeatures);
            bigLayer.setSource(bigSource);
            $scope.bigCount = bigFeatures.length;
            $scope.bigTime = (end - start) / 1000;
            var n = new Number($scope.bigCount/$scope.imgCount);
            $scope.bigPercent = n.toFixed(3);
            var viewArea = document.getElementById('map').offsetHeight *
                           document.getElementById('map').offsetWidth;
            var c = new Number($scope.bigCount*imgSize*imgSize/viewArea);
            $scope.bigCov = c.toFixed(3);

            var d = new Date();
            var start = d.getTime();
            var smallJSONs = mapcluster(JSONs, mapExtent, zoom+2, imgSize, dScore);
            var d = new Date();
            var end = d.getTime();
            var smallFeatures = new Array();
            smallJSONs.forEach(function(sj) {
                var smallFeature = new ol.format.GeoJSON().readFeature(sj, {
                    dataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:900913'
                });
                smallFeature.setStyle(createStyle(sj.properties.imgUrl, imgSize/4));
                smallFeatures.push(smallFeature);
            });
            var smallSource = new ol.source.Vector({
                projection: 'EPSG:900913'
            });
            smallSource.addFeatures(smallFeatures);
            smallLayer.setSource(smallSource);
            $scope.smallCount = smallFeatures.length;
            $scope.smallTime = (end - start) / 1000;
            var n = new Number($scope.smallCount/$scope.imgCount);
            $scope.smallPercent = n.toFixed(3);
            var c = new Number($scope.smallCount*imgSize*imgSize/viewArea/4);
            $scope.smallCov = c.toFixed(3);

            var grids = drawGrid(zoom, mapExtent, imgSize);
            var gridSource = new ol.source.Vector({
                projection: 'EPSG:900913'
            });
            gridSource.addFeatures(grids);
            gridLayer.setSource(gridSource);
        });
    }

    $scope.onSizeChange = redraw;
    $scope.onDSChange = redraw;
    map.getView().on('change:resolution', redraw);
    map.on('moveend', redraw);
});<commit_msg>initial commit to test new version<commit_after>/* global mapcluster */
/// <reference path="../../typings/angularjs/angular.d.ts""/>
/// <reference path="../../typings/ol3/ol.d.ts""/>
var MERC = 40075016.68;
var HALF_MERC = 20037508.34;
var PI = 245850922 / 78256779;
var MAX_ZOOM = 20;
var MAX_RESOLUTION = 156543.033906;

function createStyle(uri, size) {
    var img = new ol.style.Icon({
        src: '/images/' + uri,
        size: [size,size],
        offset: [0,0],
        offsetOrigin: 'top-left'
    });
    var style = new ol.style.Style({
        image: img
    });
    return style;
}

function drawGrid(zoom, extent, size) {
    size = size || 40;
    var level = zoom;
    var resolution = MAX_RESOLUTION / Math.pow(2, level);
    var gridLen;
    do {
        gridLen = HALF_MERC / Math.pow(2, level);
        var imgLen = size * resolution;
        var outer = gridLen * 2 / 3;
        var inner = gridLen * 1 / 3;
        if (imgLen > outer)
            --level;
        if (imgLen < inner)
            ++level;
    } while (imgLen > outer || imgLen < inner)
    var xmin = extent[0] - gridLen;
    var xmax = extent[2] + gridLen;
    var ymin = extent[1] - gridLen;
    var ymax = extent[3] + gridLen;
    var lines = new Array();
    for (var i = xmin; i <=xmax; i += gridLen) {
        var line = new ol.geom.LineString([[i,ymin], [i,ymax]]);
        lines.push(new ol.Feature(line));
    }
    for (var i = ymin; i <=ymax; i += gridLen) {
        var line = new ol.geom.LineString([[xmin,i], [xmax,i]]);
        lines.push(new ol.Feature(line));
    }
    return lines;
}

var map = new ol.Map({
    view: new ol.View({
        center: [0, 0],
        zoom: 3,
        maxZoom: 20,
        minZoom: 2
    }),
    layers: [
        new ol.layer.Tile({
            source: new ol.source.OSM(),
            title: 'Base OSM'
        })
    ],
    target: 'map'
});
map.addControl(new ol.control.ZoomSlider);
map.addControl(new ol.control.LayerSwitcher);

/*var gridLayer = new ol.layer.Vector({
    title: 'Grid Layer'
});
map.addLayer(gridLayer);*/
var fill = new ol.style.Fill({
    color: [255,255,255,0.4]
});
var stroke = new ol.style.Stroke({
    color: [0,150,255,1],
    width: 1.0
});
var originLayer = new ol.layer.Vector({
    title: 'Origin Layer',
    style: new ol.style.Style({
        fill: fill,
        stroke: stroke,
        image: new ol.style.Circle({
            radius: 3,
            fill: fill,
            stroke: stroke
        })
    })
});
map.addLayer(originLayer);
/*var smallLayer = new ol.layer.Vector({
    title: 'Small Layer'
});
map.addLayer(smallLayer);*/
var bigLayer = new ol.layer.Vector({
    title: 'Big Layer'
});
map.addLayer(bigLayer);

var app = angular.module('myApp', []);
app.controller('mapCtrl', function($scope, $http) {
    // uncomment this when you import geojson files into mongodb
    // and need to initialize feature properties, including mortonCode
    /* $http.get('http://localhost:3000/features/all').success(function(res) {
        res.forEach(function(fj) {
            var url = 'http://localhost:3000/features/update/' + fj.id;
            $http.post(url, {feature: fj});
        });
    }); */
    function redraw() {
        var imgSize = $scope.imgSize || 40;
        var dScore = $scope.dScore || 9;
        var mapExtent = map.getView().calculateExtent(map.getSize());
        var zoom = map.getView().getZoom();
        var gridLen = HALF_MERC / Math.pow(2, zoom + 1);
        var promise = $http({url: 'http://localhost:3000/features',
                             method: 'GET',
                             params: {xmin: mapExtent[0] - gridLen,
                                      ymin: mapExtent[1] - gridLen,
                                      xmax: mapExtent[2] + gridLen,
                                      ymax: mapExtent[3] + gridLen}});
        promise.success(function(JSONs) {
            var originSource = new ol.source.Vector();
            var originFeatures = new Array();
            JSONs.forEach(function(fj) {
                var originFeature = new ol.format.GeoJSON().readFeature(fj, {
                    dataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:900913'
                });
                originFeatures.push(originFeature);
            });
            originSource.addFeatures(originFeatures);
            originLayer.setSource(originSource);
            $scope.imgCount = originFeatures.length;

            var d = new Date();
            var start = d.getTime();
            var bigJSONs = mapcluster(JSONs, mapExtent, zoom, imgSize, dScore);
            var d = new Date();
            var end = d.getTime();
            var bigFeatures = new Array();
            bigJSONs.forEach(function(bj) {
                var bigFeature = new ol.format.GeoJSON().readFeature(bj, {
                    dataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:900913'
                });
                bigFeature.setStyle(createStyle(bj.properties.imgUrl, imgSize));
                bigFeatures.push(bigFeature);
            });
            var bigSource = new ol.source.Vector({
                projection: 'EPSG:900913'
            });
            bigSource.addFeatures(bigFeatures);
            bigLayer.setSource(bigSource);
            $scope.bigCount = bigFeatures.length;
            $scope.bigTime = (end - start) / 1000;
            var n = new Number($scope.bigCount/$scope.imgCount);
            $scope.bigPercent = n.toFixed(3);
            var viewArea = document.getElementById('map').offsetHeight *
                           document.getElementById('map').offsetWidth;
            var c = new Number($scope.bigCount*imgSize*imgSize/viewArea);
            $scope.bigCov = c.toFixed(3);

            /*var d = new Date();
            var start = d.getTime();
            var smallJSONs = mapcluster(JSONs, mapExtent, zoom+2, imgSize, dScore);
            var d = new Date();
            var end = d.getTime();
            var smallFeatures = new Array();
            smallJSONs.forEach(function(sj) {
                var smallFeature = new ol.format.GeoJSON().readFeature(sj, {
                    dataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:900913'
                });
                smallFeature.setStyle(createStyle(sj.properties.imgUrl, imgSize/4));
                smallFeatures.push(smallFeature);
            });
            var smallSource = new ol.source.Vector({
                projection: 'EPSG:900913'
            });
            smallSource.addFeatures(smallFeatures);
            smallLayer.setSource(smallSource);
            $scope.smallCount = smallFeatures.length;
            $scope.smallTime = (end - start) / 1000;
            var n = new Number($scope.smallCount/$scope.imgCount);
            $scope.smallPercent = n.toFixed(3);
            var c = new Number($scope.smallCount*imgSize*imgSize/viewArea/4);
            $scope.smallCov = c.toFixed(3);

            var grids = drawGrid(zoom, mapExtent, imgSize);
            var gridSource = new ol.source.Vector({
                projection: 'EPSG:900913'
            });
            gridSource.addFeatures(grids);
            gridLayer.setSource(gridSource);*/
        });
    }

    $scope.onSizeChange = redraw;
    $scope.onDSChange = redraw;
    map.getView().on('change:resolution', redraw);
    map.on('moveend', redraw);
});<|endoftext|>


<commit_before>module.exports = [
    {
        name: 'Narvar',
        url: 'https://corp.narvar.com/',
        logo: 'https://corp.narvar.com/wp-content/themes/narvarinc/dist/images/narvar-logo.svg',
    },
    {
        name: 'Giggso',
        url: 'https://giggso.com/',
        logo: 'https://pbs.twimg.com/profile_banners/4179602009/1470596851/600x200'
    },
    {
        name: 'STICorp',
        url: 'https://www.sticorp.com.br/',
        logo: 'https://www.sticorp.com.br/wp-content/uploads/2015/09/logo-horizontal.png',
    },
    {
        name: 'Verizon Media',
        url: 'https://www.verizonmedia.com/',
        logo: 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/09/Verizon_Media_2019_logo.svg/1200px-Verizon_Media_2019_logo.svg.png',
        featured: 1
    },
    {
        name: 'Yahoo! JAPAN',
        url: 'https://www.yahoo.co.jp/',
        logo: 'https://s.yimg.jp/images/top/sp2/cmn/logo-170307.png',
        featured: 2
    },
    {
        name: 'Zhaopin',
        url: 'https://www.zhaopin.com/',
        logo: 'https://fecdn5.zhaopin.cn/www/assets/zhaopin.4659478df1ec9a2e6ed76997a64884f6.png'
    },
    {
        name: 'Onde',
        url: 'https://onde.app/',
        logo: 'https://pbs.twimg.com/profile_images/1142024893229346818/JlCmThRZ_200x200.png',
    },
    {
        name: 'Overstock',
        url: 'https://www.overstock.com/',
        logo: 'https://ak1.ostkcdn.com/img/mxc/OSTK_MAIN_LOGO_20181127KCH.jpg'
    },
    {
        name: 'Ndustrial.io',
        url: 'https://www.ndustrial.io/',
        logo: 'https://images.squarespace-cdn.com/content/v1/56df41f2f699bb94c7a2c6fc/1552657060081-WX86GJXHX7UK81RGE6YU/ke17ZwdGBToddI8pDm48kKCofG1A85AnHnyK5g0bGkJZw-zPPgdn4jUwVcJE1ZvWQUxwkmyExglNqGp0IvTJZUJFbgE-7XRK3dMEBRBhUpzRPjxD400fzchYtVcY_asIzZyz67rHxPkx39rBmDbrxrXjNbFEDrPRRKbjQDhrWWQ/ndustrial.png'
    },
    {
        name: 'Nutanix',
        url: 'https://www.nutanix.com/',
        logo: 'https://www.zuora.com/wp-content/uploads/2017/05/nutanix.png',
        featured: 4
    },
    {
        name: 'The Hut Group',
        url: 'https://www.thg.com',
        logo: 'https://s3-eu-west-1.amazonaws.com/thehutgroup-uploads-test/app/uploads/2016/10/THG.jpg'
    },
    {
        name: 'Turtlequeue',
        url: 'https://turtlequeue.com',
        logo: 'https://turtlequeue.com/logo_black.png'
    },
    {
        name: 'ProxyClick',
        url: 'https://www.proxyclick.com/',
        logo: 'https://www.proxyclick.com/hubfs/assets/images/logo-positive.svg'
    },
    {
        name: 'Toast',
        url: 'https://toasttab.com',
        logo: 'https://cdn2.hubspot.net/hubfs/412971/toast-restaurant-pos.png'
    },
    {
        name: 'Quantel AI',
        url: 'http://quantel.ai/',
        logo: 'https://quantel.ai/wp-content/uploads/2019/09/Quantel-AI-Black-Font-Trans-1024x1024.png'
    },
    {
        name: 'Streamlio',
        url: 'https://streaml.io/',
        logo: 'https://streaml.io/media/logo/Streamlio-Logo.svg'
    },
    {
        name: 'Stream Native',
        url: 'https://streamnative.io/',
        logo: 'https://streamnative.io/images/logo.png'
    },
    {
        name: 'EMQ',
        url: 'https://www.emqx.io/',
        logo: 'https://www.emqx.io/static/img/logo.svg'
    },
    {
        name: 'Tencent',
        url: 'https://www.tencent.com',
        logo: 'http://pluspng.com/img-png/tencent-png-tencent-logo-logotype-emblem-2-7537.png',
        featured: 3
    },
    {
        name: 'Comcast',
        url: 'https://corporate.comcast.com/',
        logo: 'https://corporate.comcast.com/resources/corporate/assets/images/svg/logo-desktop.svg'
    },
    {
        name: 'China Mobile',
        url: 'https://www.chinamobileltd.com/',
        logo: 'https://www.chinamobileltd.com/en/images/global/logo.svg'
    },
    {
        name: 'VipKid',
        url: 'https://t.vipkid.com.cn/',
        logo: 'https://s.vipkidstatic.com/fe-static/teacher/mkt-pc/img/e74f189.svg'
    },
    {
        name: 'IoTium',
        url: 'https://www.iotium.io/',
        logo: 'https://www.iotium.io/site/wp-content/uploads/2018/04/IoTium-logo.jpg'
    },
    {
        name: 'One Click Retail',
        url: 'https://oneclickretail.com/',
        logo: 'https://www.ascentialedge.com/themes/contrib/custom/images/edge-by-ascential.svg',
        logo_white: true
    },
    {
        name: 'Tuya',
        url: 'https://en.tuya.com/',
        logo: 'https://static1.tuyaus.com/static/portal_intl/img/e25eefa.png'
    },
    {
        name: 'China Telecom',
        url: 'https://www.chinatelecom-h.com/',
        logo: 'https://www.chinatelecom-h.com/en/images/global/logo.png'
    },
    {
        name: 'Kafkaesque',
        url: 'https://kafkaesque.io/',
        logo: 'https://kafkaesque.io/wp-content/uploads/2019/04/color_logo_with_background.svg'
    },
    {
        name: 'OKCoin',
        url: 'https://www.okcoin.com/',
        logo: 'http://img.bafang.com/cdn/assets/imgs/MjAxOTM/784CCB28D9026D43DA6A5F257DA74BF2.png',
        logo_white: true
    },
    {
        name: 'BestPay',
        url: 'https://www.bestpay.com.cn/',
        logo: 'https://www.bestpay.com.cn/global/oig/images/logo.png'
    },
    {
        name: '360',
        url: 'https://www.360.cn/',
        logo: 'http://p2.ssl.qhimg.com/t01d91636862957f76e.png'
    },
    {
        name: 'Lakala',
        url: 'http://www.lakala.com/',
        logo: 'http://www.lakala.com/templets/default/image/temp/logo.png'
    },
    {
        name: 'Huya',
        url: 'https://www.huya.com/',
        logo: 'https://a.msstatic.com/huya/main/img/logo2.png',
        logo_white: true
    },
    {
        name: 'CTWing',
        url: 'https://www.ctwing.cn/',
        logo: 'https://www.ctwing.cn/static/img/logo.168f8b7.svg'
    },
    {
        name: 'Globalegrow E-Commerce',
        url: 'https://www.globalegrow.com/',
        logo: 'https://static.globalegrow.com/dist/images/domeimg/ch/logo.jpg'
    },
    {
        name: 'YunZhiHuLian',
        url: 'https://www.weihudashi.com',
        logo: 'https://www.weihudashi.com/image/header-logo.png'
    },
    {
        name: 'XiaoDuoAI',
        url: 'https://www.xiaoduoai.com/',
        logo: 'https://cdn.xiaoduoai.com/official-site/dist/assets/img/indexPage/xiaoduo_logo_en_white.843927.png',
        logo_white: true
    },
    {
        name: 'Shihong',
        url: 'https://shihong.net.cn',
        logo: 'https://shihong.net.cn/website/static/src/img/shihong1.png'
    },
    {
        name: 'CleverCloud',
        url: 'https://www.clever-cloud.com/en/',
        logo: 'https://www.clever-cloud.com/images/brand-assets/logos/v2/logo_on_white.svg'
    },
    {
        name: 'OVHcloud',
        url: 'https://www.ovhcloud.com/',
        logo: 'https://www.ovh.com/world/images/logo/ovhLogoColor.svg'
    },
    {
        name: 'BrandsEye',
        url: 'https://www.brandseye.com/',
        logo: 'https://www.brandseye.com/theme-images/brandseye-logo-v2.png'
    },
    {
        name: 'Max Kelsen',
        url: 'https://maxkelsen.com/',
        logo: 'https://maxkelsen.com/imgs/logo/Max-Kelsen-logo-black.png'
    },
    {
        name: 'HSL Developer Community of Helsinki Regional Transport Authority',
        url: 'https://dev.hsl.fi/',
        logo: 'https://pbs.twimg.com/profile_images/2507371771/9toyr6r5ngcozfwg42zv_400x400.png'
    },
    {
        name: 'Ottoo',
        url: 'https://ottoo.com',
        logo: 'https://ottoo.com/assets/images/ottoo-logo.svg'
    },
    {
        name: 'MercadoLibre',
        url: 'https://www.mercadolibre.com/',
        logo: 'https://d1.awsstatic.com/case-studies/LATAM/mercadolibre.1d9d3766bb06e60948f1c5ce8851ce0eac63b349.png'
    }
].sort((a, b) => (a.name > b.name) ? 1 : -1);
<commit_msg>fix powered-by: ottoo, streamnative, navar (#6651)<commit_after>module.exports = [
    {
        name: 'Narvar',
        url: 'https://corp.narvar.com/',
        logo: 'https://assets-global.website-files.com/5d278e4646ba9e55d6c67475/5d49ce69c03a64b2d8466816_Logo%20-%20Colored.svg',
    },
    {
        name: 'Giggso',
        url: 'https://giggso.com/',
        logo: 'https://pbs.twimg.com/profile_banners/4179602009/1470596851/600x200'
    },
    {
        name: 'STICorp',
        url: 'https://www.sticorp.com.br/',
        logo: 'https://www.sticorp.com.br/wp-content/uploads/2015/09/logo-horizontal.png',
    },
    {
        name: 'Verizon Media',
        url: 'https://www.verizonmedia.com/',
        logo: 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/09/Verizon_Media_2019_logo.svg/1200px-Verizon_Media_2019_logo.svg.png',
        featured: 1
    },
    {
        name: 'Yahoo! JAPAN',
        url: 'https://www.yahoo.co.jp/',
        logo: 'https://s.yimg.jp/images/top/sp2/cmn/logo-170307.png',
        featured: 2
    },
    {
        name: 'Zhaopin',
        url: 'https://www.zhaopin.com/',
        logo: 'https://fecdn5.zhaopin.cn/www/assets/zhaopin.4659478df1ec9a2e6ed76997a64884f6.png'
    },
    {
        name: 'Onde',
        url: 'https://onde.app/',
        logo: 'https://pbs.twimg.com/profile_images/1142024893229346818/JlCmThRZ_200x200.png',
    },
    {
        name: 'Overstock',
        url: 'https://www.overstock.com/',
        logo: 'https://ak1.ostkcdn.com/img/mxc/OSTK_MAIN_LOGO_20181127KCH.jpg'
    },
    {
        name: 'Ndustrial.io',
        url: 'https://www.ndustrial.io/',
        logo: 'https://images.squarespace-cdn.com/content/v1/56df41f2f699bb94c7a2c6fc/1552657060081-WX86GJXHX7UK81RGE6YU/ke17ZwdGBToddI8pDm48kKCofG1A85AnHnyK5g0bGkJZw-zPPgdn4jUwVcJE1ZvWQUxwkmyExglNqGp0IvTJZUJFbgE-7XRK3dMEBRBhUpzRPjxD400fzchYtVcY_asIzZyz67rHxPkx39rBmDbrxrXjNbFEDrPRRKbjQDhrWWQ/ndustrial.png'
    },
    {
        name: 'Nutanix',
        url: 'https://www.nutanix.com/',
        logo: 'https://www.zuora.com/wp-content/uploads/2017/05/nutanix.png',
        featured: 4
    },
    {
        name: 'The Hut Group',
        url: 'https://www.thg.com',
        logo: 'https://s3-eu-west-1.amazonaws.com/thehutgroup-uploads-test/app/uploads/2016/10/THG.jpg'
    },
    {
        name: 'Turtlequeue',
        url: 'https://turtlequeue.com',
        logo: 'https://turtlequeue.com/logo_black.png'
    },
    {
        name: 'ProxyClick',
        url: 'https://www.proxyclick.com/',
        logo: 'https://www.proxyclick.com/hubfs/assets/images/logo-positive.svg'
    },
    {
        name: 'Toast',
        url: 'https://toasttab.com',
        logo: 'https://cdn2.hubspot.net/hubfs/412971/toast-restaurant-pos.png'
    },
    {
        name: 'Quantel AI',
        url: 'http://quantel.ai/',
        logo: 'https://quantel.ai/wp-content/uploads/2019/09/Quantel-AI-Black-Font-Trans-1024x1024.png'
    },
    {
        name: 'Streamlio',
        url: 'https://streaml.io/',
        logo: 'https://streaml.io/media/logo/Streamlio-Logo.svg'
    },
    {
        name: 'Stream Native',
        url: 'https://streamnative.io/',
        logo: 'https://streamnative.io/streamnative-logo.png',
        featured: 3
    },
    {
        name: 'EMQ',
        url: 'https://www.emqx.io/',
        logo: 'https://www.emqx.io/static/img/logo.svg'
    },
    {
        name: 'Tencent',
        url: 'https://www.tencent.com',
        logo: 'http://pluspng.com/img-png/tencent-png-tencent-logo-logotype-emblem-2-7537.png',
        featured: 3
    },
    {
        name: 'Comcast',
        url: 'https://corporate.comcast.com/',
        logo: 'https://corporate.comcast.com/resources/corporate/assets/images/svg/logo-desktop.svg'
    },
    {
        name: 'China Mobile',
        url: 'https://www.chinamobileltd.com/',
        logo: 'https://www.chinamobileltd.com/en/images/global/logo.svg'
    },
    {
        name: 'VipKid',
        url: 'https://t.vipkid.com.cn/',
        logo: 'https://s.vipkidstatic.com/fe-static/teacher/mkt-pc/img/e74f189.svg'
    },
    {
        name: 'IoTium',
        url: 'https://www.iotium.io/',
        logo: 'https://www.iotium.io/site/wp-content/uploads/2018/04/IoTium-logo.jpg'
    },
    {
        name: 'One Click Retail',
        url: 'https://oneclickretail.com/',
        logo: 'https://www.ascentialedge.com/themes/contrib/custom/images/edge-by-ascential.svg',
        logo_white: true
    },
    {
        name: 'Tuya',
        url: 'https://en.tuya.com/',
        logo: 'https://static1.tuyaus.com/static/portal_intl/img/e25eefa.png'
    },
    {
        name: 'China Telecom',
        url: 'https://www.chinatelecom-h.com/',
        logo: 'https://www.chinatelecom-h.com/en/images/global/logo.png'
    },
    {
        name: 'Kafkaesque',
        url: 'https://kafkaesque.io/',
        logo: 'https://kafkaesque.io/wp-content/uploads/2019/04/color_logo_with_background.svg'
    },
    {
        name: 'OKCoin',
        url: 'https://www.okcoin.com/',
        logo: 'http://img.bafang.com/cdn/assets/imgs/MjAxOTM/784CCB28D9026D43DA6A5F257DA74BF2.png',
        logo_white: true
    },
    {
        name: 'BestPay',
        url: 'https://www.bestpay.com.cn/',
        logo: 'https://www.bestpay.com.cn/global/oig/images/logo.png'
    },
    {
        name: '360',
        url: 'https://www.360.cn/',
        logo: 'http://p2.ssl.qhimg.com/t01d91636862957f76e.png'
    },
    {
        name: 'Lakala',
        url: 'http://www.lakala.com/',
        logo: 'http://www.lakala.com/templets/default/image/temp/logo.png'
    },
    {
        name: 'Huya',
        url: 'https://www.huya.com/',
        logo: 'https://a.msstatic.com/huya/main/img/logo2.png',
        logo_white: true
    },
    {
        name: 'CTWing',
        url: 'https://www.ctwing.cn/',
        logo: 'https://www.ctwing.cn/static/img/logo.168f8b7.svg'
    },
    {
        name: 'Globalegrow E-Commerce',
        url: 'https://www.globalegrow.com/',
        logo: 'https://static.globalegrow.com/dist/images/domeimg/ch/logo.jpg'
    },
    {
        name: 'YunZhiHuLian',
        url: 'https://www.weihudashi.com',
        logo: 'https://www.weihudashi.com/image/header-logo.png'
    },
    {
        name: 'XiaoDuoAI',
        url: 'https://www.xiaoduoai.com/',
        logo: 'https://cdn.xiaoduoai.com/official-site/dist/assets/img/indexPage/xiaoduo_logo_en_white.843927.png',
        logo_white: true
    },
    {
        name: 'Shihong',
        url: 'https://shihong.net.cn',
        logo: 'https://shihong.net.cn/website/static/src/img/shihong1.png'
    },
    {
        name: 'CleverCloud',
        url: 'https://www.clever-cloud.com/en/',
        logo: 'https://www.clever-cloud.com/images/brand-assets/logos/v2/logo_on_white.svg'
    },
    {
        name: 'OVHcloud',
        url: 'https://www.ovhcloud.com/',
        logo: 'https://www.ovh.com/world/images/logo/ovhLogoColor.svg'
    },
    {
        name: 'BrandsEye',
        url: 'https://www.brandseye.com/',
        logo: 'https://www.brandseye.com/theme-images/brandseye-logo-v2.png'
    },
    {
        name: 'Max Kelsen',
        url: 'https://maxkelsen.com/',
        logo: 'https://maxkelsen.com/imgs/logo/Max-Kelsen-logo-black.png'
    },
    {
        name: 'HSL Developer Community of Helsinki Regional Transport Authority',
        url: 'https://dev.hsl.fi/',
        logo: 'https://pbs.twimg.com/profile_images/2507371771/9toyr6r5ngcozfwg42zv_400x400.png'
    },
    // {
    //     name: 'Ottoo',
    //     url: 'https://ottoo.com',
    //     logo: 'https://ottoo.com/assets/images/ottoo-logo.svg'
    // },
    {
        name: 'MercadoLibre',
        url: 'https://www.mercadolibre.com/',
        logo: 'https://d1.awsstatic.com/case-studies/LATAM/mercadolibre.1d9d3766bb06e60948f1c5ce8851ce0eac63b349.png'
    }
].sort((a, b) => (a.name > b.name) ? 1 : -1);
<|endoftext|>