<commit_before>/* eslint-env node */

var Ping = require('./ping');
var realtimeSetInterval = require('./realtime-set-interval');
var _ = require('underscore');
var Backbone = require('backbone');

module.exports = function() {
    var list = [];
    var max = 100;
    var lastRespondedPing;

    /**
     * The amount of active pings (i.e. connections) that can run concurrently.
     *
     * This should solve the problem of the connection being restored after
     * being offline for a while (thus, there being a lot of open connections),
     * and new pings not working as the browser already reached the limit
     * of open connections.
     *
     * @type {integer}
     */
    var pingsConcurrencyLimit = 20;

    var getFirstOfTheLastUnrespondedPings = function() {
        var first;
        for (var i = list.length - 1; i >= 0; i--) {
            if (list[i].end) break;
            first = list[i];
        }
        return first;
    };

    var getRunningPings = function () {
        return list.filter(function (ping) { return !ping.done; });
    };

    var pings = {
        interval: 1000 /* msecs */, // How often pings are created
        all: function() {
            return list;
        },
        setMax: function(_max) {
            max = _max;
        },
        max: function() {
            return max;
        },
        currentLag: function() {
            var lastRespondedPingLag = lastRespondedPing ? lastRespondedPing.lag() : 0;

            var firstOfTheLastUnrespondedPingsLag = (function() {
                var ping = getFirstOfTheLastUnrespondedPings();
                return ping ? ping.lag() : 0;
            })();

            return Math.max(lastRespondedPingLag, firstOfTheLastUnrespondedPingsLag);
        }
    };

    _.extend(pings, Backbone.Events);

    var removePingsOverLimit = function () {
        list = _.last(list, max);
    };

    var abortOldestPingsOverConcurrencyLimit = function () {
        _.chain(getRunningPings()).initial(pingsConcurrencyLimit).invoke('abort');
    };

    var addPing = function () {
        var ping = new Ping;
        ping.on('pong', function() {
            if (!lastRespondedPing || lastRespondedPing.start < ping.start) {
                lastRespondedPing = ping;
            }

            pings.trigger('pong', ping);
        });
        list.push(ping);
        pings.trigger('add', ping);
    };

    var ping = function () {
        removePingsOverLimit();
        abortOldestPingsOverConcurrencyLimit();
        addPing();
    };

    ping(); // Start pinging ASAP

    realtimeSetInterval(ping, pings.interval);

    return pings;
};
<commit_msg>Extract PINGS_CONCURRENCY_LIMIT<commit_after>/* eslint-env node */

var Ping = require('./ping');
var realtimeSetInterval = require('./realtime-set-interval');
var _ = require('underscore');
var Backbone = require('backbone');

/**
 * The amount of active pings (i.e. connections) that can run concurrently.
 *
 * This should solve the problem of the connection being restored after
 * being offline for a while (thus, there being a lot of open connections),
 * and new pings not working as the browser already reached the limit
 * of open connections.
 *
 * @type {integer}
 */
var PINGS_CONCURRENCY_LIMIT = 20;

module.exports = function() {
    var list = [];
    var max = 100;
    var lastRespondedPing;

    var getFirstOfTheLastUnrespondedPings = function() {
        var first;
        for (var i = list.length - 1; i >= 0; i--) {
            if (list[i].end) break;
            first = list[i];
        }
        return first;
    };

    var getRunningPings = function () {
        return list.filter(function (ping) { return !ping.done; });
    };

    var pings = {
        interval: 1000 /* msecs */, // How often pings are created
        all: function() {
            return list;
        },
        setMax: function(_max) {
            max = _max;
        },
        max: function() {
            return max;
        },
        currentLag: function() {
            var lastRespondedPingLag = lastRespondedPing ? lastRespondedPing.lag() : 0;

            var firstOfTheLastUnrespondedPingsLag = (function() {
                var ping = getFirstOfTheLastUnrespondedPings();
                return ping ? ping.lag() : 0;
            })();

            return Math.max(lastRespondedPingLag, firstOfTheLastUnrespondedPingsLag);
        }
    };

    _.extend(pings, Backbone.Events);

    var removePingsOverLimit = function () {
        list = _.last(list, max);
    };

    var abortOldestPingsOverConcurrencyLimit = function () {
        _.chain(getRunningPings()).initial(PINGS_CONCURRENCY_LIMIT).invoke('abort');
    };

    var addPing = function () {
        var ping = new Ping;
        ping.on('pong', function() {
            if (!lastRespondedPing || lastRespondedPing.start < ping.start) {
                lastRespondedPing = ping;
            }

            pings.trigger('pong', ping);
        });
        list.push(ping);
        pings.trigger('add', ping);
    };

    var ping = function () {
        removePingsOverLimit();
        abortOldestPingsOverConcurrencyLimit();
        addPing();
    };

    ping(); // Start pinging ASAP

    realtimeSetInterval(ping, pings.interval);

    return pings;
};
<|endoftext|>


<commit_before>/**
 * Largely based on the VirtualList component from
 * https://github.com/developerdizzle/react-virtual-list
 */

import React from 'react';
import DOMUtil from '../Util/DOMUtil';
import Util from '../Util/Util';

const VirtualList = React.createClass({
  propTypes: {
    items: React.PropTypes.array.isRequired,
    itemHeight: React.PropTypes.number.isRequired,
    onReady: React.PropTypes.func,
    renderItem: React.PropTypes.func.isRequired,
    container: React.PropTypes.object.isRequired,
    tagName: React.PropTypes.string.isRequired,
    scrollDelay: React.PropTypes.number,
    itemBuffer: React.PropTypes.number
  },

  getDefaultProps: function () {
    return {
      container: typeof window !== 'undefined' ? window : undefined,
      tagName: 'div',
      scrollDelay: 0,
      itemBuffer: 0
    };
  },

  getVirtualState: function (props) {
    // default values
    let state = {
      bufferEnd: 0,
      bufferStart: 0,
      height: 0,
      items: []
    };

    // early return if nothing to render
    if (typeof props.container === 'undefined' ||
        props.items.length === 0 ||
        props.itemHeight <= 0 ||
        !this.isMounted()) {
      return state;
    }

    let items = props.items;
    let container = props.container;
    let viewHeight = typeof container.innerHeight !== 'undefined' ? container.innerHeight : container.clientHeight;

    // no space to render
    if (viewHeight <= 0) {
      return state;
    }

    let list = this.getDOMNode();
    let offsetTop = DOMUtil.topDifference(list, container);
    let viewTop = typeof container.scrollY !== 'undefined' ? container.scrollY : container.scrollTop;
    let renderStats = VirtualList.getItems(viewTop, viewHeight, offsetTop, props.itemHeight, items.length, props.itemBuffer);

    // no items to render
    if (renderStats.itemsInView.length === 0) {
      return state;
    }

    state.height = props.items.length * props.itemHeight;
    state.items = items.slice(renderStats.firstItemIndex, renderStats.lastItemIndex + 1);
    state.bufferStart = renderStats.firstItemIndex * props.itemHeight;
    state.bufferEnd = (props.items.length - renderStats.lastItemIndex - 1) * props.itemHeight;

    return state;
  },

  getInitialState: function () {
    return this.getVirtualState(this.props);
  },

  componentWillReceiveProps: function (nextProps) {
    let state = this.getVirtualState(nextProps);

    this.props.container.removeEventListener('scroll', this.onScrollDebounced);

    this.onScrollDebounced = Util.debounce(this.onScroll, nextProps.scrollDelay, false);

    nextProps.container.addEventListener('scroll', this.onScrollDebounced);

    this.setState(state);
  },

  componentWillMount: function () {
    this.onScrollDebounced = Util.debounce(this.onScroll, this.props.scrollDelay, false);
  },

  componentDidMount: function () {
    let state = this.getVirtualState(this.props);
    let onReady = this.props.onReady || Util.noop;
    this.setState(state, onReady);

    this.props.container.addEventListener('scroll', this.onScrollDebounced);
  },

  componentWillUnmount: function () {
    this.props.container.removeEventListener('scroll', this.onScrollDebounced);
  },

  onScroll: function () {
    let state = this.getVirtualState(this.props);
    this.setState(state);
  },

  // in case you need to get the currently visible items
  visibleItems: function () {
    return this.state.items;
  },

  render: function () {

    let state = this.state;
    let props = this.props;

    let topStyles = {
      height: state.bufferStart
    };

    if (!(state.bufferStart > 0)) {
      topStyles.display = 'none';
    }

    let bottomStyles = {
      height: state.bufferEnd
    };

    if (!(state.bufferEnd > 0)) {
      bottomStyles.display = 'none';
    }

    return (
    <props.tagName {...props}>
      {props.renderBufferItem(topStyles)}
      {state.items.map(props.renderItem)}
      {props.renderBufferItem(bottomStyles)}
    </props.tagName>
    );
  }
});

VirtualList.getBox = function (view, list) {
  list.height = list.height || list.bottom - list.top;

  return {
    top: Math.max(0, Math.min(view.top - list.top)),
    bottom: Math.max(0, Math.min(list.height, view.bottom - list.top))
  };
};

VirtualList.getItems = function (viewTop, viewHeight, listTop, itemHeight, itemCount, itemBuffer) {
  if (itemCount === 0 || itemHeight === 0) {
    return {
      itemsInView: 0
    };
  }

  let listHeight = itemHeight * itemCount;

  let listBox = {
    top: listTop,
    height: listHeight,
    bottom: listTop + listHeight
  };

  let bufferHeight = itemBuffer * itemHeight;
  viewTop -= bufferHeight;
  viewHeight += bufferHeight * 2;

  let viewBox = {
    top: viewTop,
    bottom: viewTop + viewHeight
  };

  // list is below viewport
  if (viewBox.bottom < listBox.top) {
    return {
      itemsInView: 0
    };
  }

  // list is above viewport
  if (viewBox.top > listBox.bottom) {
    return {
      itemsInView: 0
    };
  }

  let listViewBox = VirtualList.getBox(viewBox, listBox);

  let firstItemIndex = Math.max(0, Math.floor(listViewBox.top / itemHeight));
  let lastItemIndex = Math.ceil(listViewBox.bottom / itemHeight) - 1;

  let itemsInView = lastItemIndex - firstItemIndex + 1;

  let result = {
    firstItemIndex: firstItemIndex,
    lastItemIndex: lastItemIndex,
    itemsInView: itemsInView
  };

  return result;
};

module.exports = VirtualList;
<commit_msg>Ignoring setState in didMount for VirtualList<commit_after>/**
 * Largely based on the VirtualList component from
 * https://github.com/developerdizzle/react-virtual-list
 */

/* eslint react/no-did-mount-set-state: 0 */
import React from 'react';
import DOMUtil from '../Util/DOMUtil';
import Util from '../Util/Util';

const VirtualList = React.createClass({
  propTypes: {
    items: React.PropTypes.array.isRequired,
    itemHeight: React.PropTypes.number.isRequired,
    onReady: React.PropTypes.func,
    renderItem: React.PropTypes.func.isRequired,
    container: React.PropTypes.object.isRequired,
    tagName: React.PropTypes.string.isRequired,
    scrollDelay: React.PropTypes.number,
    itemBuffer: React.PropTypes.number
  },

  getDefaultProps: function () {
    return {
      container: typeof window !== 'undefined' ? window : undefined,
      tagName: 'div',
      scrollDelay: 0,
      itemBuffer: 0
    };
  },

  getVirtualState: function (props) {
    // default values
    let state = {
      bufferEnd: 0,
      bufferStart: 0,
      height: 0,
      items: []
    };

    // early return if nothing to render
    if (typeof props.container === 'undefined' ||
        props.items.length === 0 ||
        props.itemHeight <= 0 ||
        !this.isMounted()) {
      return state;
    }

    let items = props.items;
    let container = props.container;
    let viewHeight = typeof container.innerHeight !== 'undefined' ? container.innerHeight : container.clientHeight;

    // no space to render
    if (viewHeight <= 0) {
      return state;
    }

    let list = this.getDOMNode();
    let offsetTop = DOMUtil.topDifference(list, container);
    let viewTop = typeof container.scrollY !== 'undefined' ? container.scrollY : container.scrollTop;
    let renderStats = VirtualList.getItems(viewTop, viewHeight, offsetTop, props.itemHeight, items.length, props.itemBuffer);

    // no items to render
    if (renderStats.itemsInView.length === 0) {
      return state;
    }

    state.height = props.items.length * props.itemHeight;
    state.items = items.slice(renderStats.firstItemIndex, renderStats.lastItemIndex + 1);
    state.bufferStart = renderStats.firstItemIndex * props.itemHeight;
    state.bufferEnd = (props.items.length - renderStats.lastItemIndex - 1) * props.itemHeight;

    return state;
  },

  getInitialState: function () {
    return this.getVirtualState(this.props);
  },

  componentWillReceiveProps: function (nextProps) {
    let state = this.getVirtualState(nextProps);

    this.props.container.removeEventListener('scroll', this.onScrollDebounced);

    this.onScrollDebounced = Util.debounce(this.onScroll, nextProps.scrollDelay, false);

    nextProps.container.addEventListener('scroll', this.onScrollDebounced);

    this.setState(state);
  },

  componentWillMount: function () {
    this.onScrollDebounced = Util.debounce(this.onScroll, this.props.scrollDelay, false);
  },

  componentDidMount: function () {
    let state = this.getVirtualState(this.props);
    let onReady = this.props.onReady || Util.noop;
    this.setState(state, onReady);

    this.props.container.addEventListener('scroll', this.onScrollDebounced);
  },

  componentWillUnmount: function () {
    this.props.container.removeEventListener('scroll', this.onScrollDebounced);
  },

  onScroll: function () {
    let state = this.getVirtualState(this.props);
    this.setState(state);
  },

  // in case you need to get the currently visible items
  visibleItems: function () {
    return this.state.items;
  },

  render: function () {

    let state = this.state;
    let props = this.props;

    let topStyles = {
      height: state.bufferStart
    };

    if (!(state.bufferStart > 0)) {
      topStyles.display = 'none';
    }

    let bottomStyles = {
      height: state.bufferEnd
    };

    if (!(state.bufferEnd > 0)) {
      bottomStyles.display = 'none';
    }

    return (
    <props.tagName {...props}>
      {props.renderBufferItem(topStyles)}
      {state.items.map(props.renderItem)}
      {props.renderBufferItem(bottomStyles)}
    </props.tagName>
    );
  }
});

VirtualList.getBox = function (view, list) {
  list.height = list.height || list.bottom - list.top;

  return {
    top: Math.max(0, Math.min(view.top - list.top)),
    bottom: Math.max(0, Math.min(list.height, view.bottom - list.top))
  };
};

VirtualList.getItems = function (viewTop, viewHeight, listTop, itemHeight, itemCount, itemBuffer) {
  if (itemCount === 0 || itemHeight === 0) {
    return {
      itemsInView: 0
    };
  }

  let listHeight = itemHeight * itemCount;

  let listBox = {
    top: listTop,
    height: listHeight,
    bottom: listTop + listHeight
  };

  let bufferHeight = itemBuffer * itemHeight;
  viewTop -= bufferHeight;
  viewHeight += bufferHeight * 2;

  let viewBox = {
    top: viewTop,
    bottom: viewTop + viewHeight
  };

  // list is below viewport
  if (viewBox.bottom < listBox.top) {
    return {
      itemsInView: 0
    };
  }

  // list is above viewport
  if (viewBox.top > listBox.bottom) {
    return {
      itemsInView: 0
    };
  }

  let listViewBox = VirtualList.getBox(viewBox, listBox);

  let firstItemIndex = Math.max(0, Math.floor(listViewBox.top / itemHeight));
  let lastItemIndex = Math.ceil(listViewBox.bottom / itemHeight) - 1;

  let itemsInView = lastItemIndex - firstItemIndex + 1;

  let result = {
    firstItemIndex: firstItemIndex,
    lastItemIndex: lastItemIndex,
    itemsInView: itemsInView
  };

  return result;
};

module.exports = VirtualList;
<|endoftext|>


<commit_before>define([
	'goo/statemachine/actions/Action'
],
/** @lends */
function(
	Action
) {
	"use strict";

	function EmitAction(/*id, settings*/) {
		Action.apply(this, arguments);
	}

	EmitAction.prototype = Object.create(Action.prototype);
	EmitAction.prototype.constructor = EmitAction;

	EmitAction.external = {
		name: 'Emit message',
		description: 'Emits a message on the bus',
		parameters: [{
			name: 'Channel',
			key: 'channel',
			type: 'string',
			description: 'Channel to transmit on',
			'default': ''
		}, {
			name: 'Data',
			key: 'data',
			type: 'string',
			description: 'Data to send',
			'default': ''
		}],
		transitions: []
	};

	EmitAction.prototype._run = function(fsm) {
		fsm.send(this.channel, this.data);
	};

	return EmitAction;
});<commit_msg>#451 EmitMessage action works<commit_after>define([
	'goo/statemachine/actions/Action',
	'goo/entities/SystemBus'
],
/** @lends */
function(
	Action,
	SystemBus
) {
	"use strict";

	function EmitAction(/*id, settings*/) {
		Action.apply(this, arguments);
	}

	EmitAction.prototype = Object.create(Action.prototype);
	EmitAction.prototype.constructor = EmitAction;

	EmitAction.external = {
		name: 'Emit message',
		description: 'Emits a message on the bus',
		parameters: [{
			name: 'Channel',
			key: 'channel',
			type: 'string',
			description: 'Channel to transmit on',
			'default': ''
		}],
		transitions: []
	};

	EmitAction.prototype._run = function (/*fsm*/) {
		SystemBus.emit(this.channel, this.data);
	};

	return EmitAction;
});<|endoftext|>


<commit_before>/*global chrome*/

// Configure the platform
var platform = {
  tcp: require('./lib/chrome-tcp.js'),
  sha1: require('git-sha1'),
  bops: require('./lib/bops/index.js'),
  // inflate: require('git-zlib/inflate.js'),
  // deflate: require('git-zlib/deflate.js'),
  // trace: require('./trace.js'),
};
platform.http = require('git-http')(platform);

// Polyfill setImmediate
if (!window.setImmediate) window.setImmediate = require('./lib/defer.js');

// Configure the backend
var backend = require('./app/backend.js')({
  repo: require('js-git')(platform),
  remote: require('git-net')(platform),
  db: require('./lib/git-chrome-localdb.js')(platform),
  // db: require('./lib/git-memdb.js'),
  settings: { get: get, set: set },
});

var sync = chrome.storage.sync;
var settings;

function get(key) {
  return settings[key];
}
function set(key, value) {
  settings[key] = value;
  var dump = {};
  dump[key] = value;
  sync.set(dump);
}

var ui = require('./app/ui.js');
ui.confirm = function (message, callback) {
  console.log("Confirm", message);
  callback(true);
};
ui.error = function (err) {
  console.error(err.stack);
};

sync.get(null, function (items) {
  settings = items;
  require('./app/phone-ui.js')(backend);
});

// /*global FileError*/
// window.requestFileSystem  = window.requestFileSystem || window.webkitRequestFileSystem;

// var platform = {
//   tcp: require('./chrome-tcp.js'),
//   sha1: require('./sha1.js'),
//   bops: require('./bops/index.js'),
//   inflate: require('./inflate.js'),
//   deflate: require('./deflate.js'),
// };

// var jsGit = require('js-git')(platform);
// var gitRemote = require('git-net')(platform);
// var fsDb = require('git-fs-db')(platform);
// var gitWebfs = require('./git-webfs.js');
// var progressParser = require('./progress-parser.js');

// var fs;
// function getFs(callback) {
//   if (fs) return callback(null, fs);
//   var done = false;
//   window.requestFileSystem(window.TEMPORARY, null, onInitFs, errorHandler);
//   function onInitFs(webfs) {
//     if (done) return;
//     done = true;
//     fs = gitWebfs(webfs.root);
//     callback(null, fs);
//   }
//   function errorHandler(err) {
//     var message = 'Unknown Error';
//     for (var key in FileError) {
//       if (!/_ERR$/.test(key)) continue;
//       if (FileError[key] === err.code) {
//         message = key;
//         break;
//       }
//     }
//     console.error('Error: ' + message);
//     if (done) return;
//     done = true;
//     callback(new Error(message));
//   }
// }

// function isGitDir(path) {
//   return (/\.git$/).test(path);
// }

// require('./main.js')({
//   // opts are host, path, and description
//   addRepo: function (opts, onProgress, callback) {
//     getFs(function (err, fs) {
//       if (err) return callback(err);
//       var url = "git://" + opts.hostname + opts.pathname;
//       var description = opts.description || url;
//       var path = (opts.hostname + opts.pathname).replace(/\//g, "_");
//       var repo = jsGit(fsDb(fs(path)));
//       var remote = gitRemote(url);
//       var name = opts.pathname;
//       if (name[0] === "/") name = name.substr(1);
//       if (name.substr(name.length - 4) === ".git") name = name.substr(0, name.length - 4);
//       repo.name = name;
//       repo.description = description;

//       repo.fetch(remote, {
//         includeTag: true,
//         onProgress: progressParser(onProgress)
//       }, function (err) {
//         if (err) return callback(err);
//         onProgress("Done");
//         callback(null, repo);
//       });
//     });
//   },
//   getRepos: function (callback) {
//     getFs(function (err, fs) {
//       if (err) return callback(err);
//       fs.readdir("/", function (err, files) {
//         if (err) return callback(err);
//         callback(null, files.filter(isGitDir).map(function (path) {
//           var repo = jsGit(fsDb(fs(path)));
//           repo.name = path;
//           repo.description = path;
//           return repo;
//         }));
//       });
//     });
//   },
//   getHistoryStream: function (repo, callback) {
//     repo.logWalk("HEAD", callback);
//   },
//   getCommit: function (repo, hash, callback) {
//     repo.loadAs("commit", hash, function (err, commit) {
//       if (err) return callback(err);
//       commit.hash = hash;
//       callback(null, commit);
//     });
//   },
//   getTree: function (repo, hash, callback) {
//     repo.loadAs("tree", hash, function (err, tree) {
//       if (err) return callback(err);
//       tree.hash = hash;
//       callback(null, tree);
//     });
//   }
// });
<commit_msg>Remove junk code<commit_after>/*global chrome*/

// Configure the platform
var platform = {
  tcp: require('./lib/chrome-tcp.js'),
  sha1: require('git-sha1'),
  bops: require('./lib/bops/index.js'),
  // inflate: require('git-zlib/inflate.js'),
  // deflate: require('git-zlib/deflate.js'),
  // trace: require('./trace.js'),
};
platform.http = require('git-http')(platform);

// Polyfill setImmediate
if (!window.setImmediate) window.setImmediate = require('./lib/defer.js');

// Configure the backend
var backend = require('./app/backend.js')({
  repo: require('js-git')(platform),
  remote: require('git-net')(platform),
  db: require('./lib/git-chrome-localdb.js')(platform),
  // db: require('./lib/git-memdb.js'),
  settings: { get: get, set: set },
});

var sync = chrome.storage.sync;
var settings;

function get(key) {
  return settings[key];
}
function set(key, value) {
  settings[key] = value;
  var dump = {};
  dump[key] = value;
  sync.set(dump);
}

var ui = require('./app/ui.js');
ui.confirm = function (message, callback) {
  console.log("Confirm", message);
  callback(true);
};
ui.error = function (err) {
  console.error(err.stack);
};

sync.get(null, function (items) {
  settings = items;
  require('./app/phone-ui.js')(backend);
});

<|endoftext|>


<commit_before>import { LineBasicMaterial, BufferAttribute, Box3, Group, LineSegments } from 'three';
import { arrayToBox } from './Utils/ArrayBoxUtilities.js';

// fill in the index buffer to point to the corner points
const edgeIndices = new Uint8Array( [
	// x axis
	0, 4,
	1, 5,
	2, 6,
	3, 7,

	// y axis
	0, 2,
	1, 3,
	4, 6,
	5, 7,

	// z axis
	0, 1,
	2, 3,
	4, 5,
	6, 7,
] );

const faceIndices = new Uint8Array( [
	0, 1, 3,
	3, 1, 2,

	1, 5, 2,
	2, 5, 6,

	5, 4, 6,
	6, 4, 7,

	4, 0, 7,
	7, 0, 3,

	3, 2, 7,
	7, 2, 6,

	4, 5, 0,
	0, 5, 1
] );

const boundingBox = new Box3();
class MeshBVHRootVisualizer extends LineSegments {

	constructor( mesh, material, depth = 10, group = 0 ) {

		super( undefined, material );

		this.material = material;
		this.name = 'MeshBVHRootVisualizer';
		this.depth = depth;
		this.displayParents = false;
		this.mesh = mesh;
		this.displayLines = true;
		this._group = group;

	}

	raycast() {}

	update() {

		const linesGeometry = this.geometry;
		const boundsTree = this.mesh.geometry.boundsTree;
		const group = this._group;
		linesGeometry.dispose();
		this.visible = false;
		if ( boundsTree ) {

			// count the number of bounds required
			const targetDepth = this.depth - 1;
			const displayParents = this.displayParents;
			let boundsCount = 0;
			boundsTree.traverse( ( depth, isLeaf ) => {

				if ( depth === targetDepth || isLeaf ) {

					boundsCount ++;
					return true;

				} else if ( displayParents ) {

					boundsCount ++;

				}

			}, group );

			// fill in the position buffer with the bounds corners
			let posIndex = 0;
			const positionArray = new Float32Array( 8 * 3 * boundsCount );
			boundsTree.traverse( ( depth, isLeaf, boundingData ) => {

				const terminate = depth === targetDepth || isLeaf;
				if ( terminate || displayParents ) {

					arrayToBox( boundingData, boundingBox );

					const { min, max } = boundingBox;
					for ( let x = - 1; x <= 1; x += 2 ) {

						const xVal = x < 0 ? min.x : max.x;
						for ( let y = - 1; y <= 1; y += 2 ) {

							const yVal = y < 0 ? min.y : max.y;
							for ( let z = - 1; z <= 1; z += 2 ) {

								const zVal = z < 0 ? min.z : max.z;
								positionArray[ posIndex + 0 ] = xVal;
								positionArray[ posIndex + 1 ] = yVal;
								positionArray[ posIndex + 2 ] = zVal;

								posIndex += 3;

							}

						}

					}

					return terminate;

				}

			}, group );

			let indexArray;
			const indexCount = edgeIndices.lengths;
			if ( positionArray.length > 65535 ) {

				indexArray = new Uint32Array( indexCount * boundsCount );

			} else {

				indexArray = new Uint16Array( indexCount * boundsCount );

			}

			for ( let i = 0; i < boundsCount; i ++ ) {

				const posOffset = i * 8;
				const indexOffset = i * 24;
				for ( let j = 0; j < 24; j ++ ) {

					indexArray[ indexOffset + j ] = posOffset + edgeIndices[ j ];

				}

			}

			// update the geometry
			linesGeometry.setIndex(
				new BufferAttribute( indexArray, 1, false ),
			);
			linesGeometry.setAttribute(
				'position',
				new BufferAttribute( positionArray, 3, false ),
			);
			this.visible = true;

		}

	}

}

class MeshBVHVisualizer extends Group {

	get color() {

		return this._material.color;

	}

	get opacity() {

		return this._material.opacity;

	}

	set opacity( v ) {

		this._material.opacity = v;

	}

	constructor( mesh, depth = 10 ) {

		super();

		this.name = 'MeshBVHVisualizer';
		this.depth = depth;
		this.mesh = mesh;
		this.displayParents = false;
		this._roots = [];
		this._material = new LineBasicMaterial( {
			color: 0x00FF88,
			transparent: true,
			opacity: 0.3,
			depthWrite: false,
		} );

		this.update();

	}

	update() {

		const bvh = this.mesh.geometry.boundsTree;
		const totalRoots = bvh ? bvh._roots.length : 0;
		while ( this._roots.length > totalRoots ) {

			this._roots.pop();

		}

		for ( let i = 0; i < totalRoots; i ++ ) {

			if ( i >= this._roots.length ) {

				const root = new MeshBVHRootVisualizer( this.mesh, this._material, this.depth, i );
				this.add( root );
				this._roots.push( root );

			}

			const root = this._roots[ i ];
			root.depth = this.depth;
			root.mesh = this.mesh;
			root.displayParents = this.displayParents;
			root.update();

		}

	}

	updateMatrixWorld( ...args ) {

		this.position.copy( this.mesh.position );
		this.rotation.copy( this.mesh.rotation );
		this.scale.copy( this.mesh.scale );

		super.updateMatrixWorld( ...args );

	}

	copy( source ) {

		this.depth = source.depth;
		this.mesh = source.mesh;

	}

	clone() {

		return new MeshBVHVisualizer( this.mesh, this.depth );

	}

	dispose() {

		this._material.dispose();

	}

}

export default MeshBVHVisualizer;
<commit_msg>solid bvh viz<commit_after>import { LineBasicMaterial, BufferAttribute, Box3, Group, LineSegments, MeshBasicMaterial, Mesh } from 'three';
import { arrayToBox } from './Utils/ArrayBoxUtilities.js';

// fill in the index buffer to point to the corner points
const edgeIndices = new Uint8Array( [
	// x axis
	0, 4,
	1, 5,
	2, 6,
	3, 7,

	// y axis
	0, 2,
	1, 3,
	4, 6,
	5, 7,

	// z axis
	0, 1,
	2, 3,
	4, 5,
	6, 7,
] );

// z, y, x
const meshIndices = new Uint8Array( [

	// X-, X+
	0, 1, 2,
	2, 1, 3,

	4, 6, 5,
	6, 7, 5,

	// Y-, Y+
	1, 4, 5,
	0, 4, 1,

	2, 3, 6,
	3, 7, 6,

	// Z-, Z+
	0, 2, 4,
	2, 6, 4,

	1, 5, 3,
	3, 5, 7,

] );

const boundingBox = new Box3();
class MeshBVHRootVisualizer extends Mesh {

	constructor( mesh, material, depth = 10, group = 0 ) {

		super( undefined, material );

		this.material = material;
		this.name = 'MeshBVHRootVisualizer';
		this.depth = depth;
		this.displayParents = false;
		this.mesh = mesh;
		this.displayEdges = true;
		this._group = group;

	}

	raycast() {}

	update() {

		const linesGeometry = this.geometry;
		const boundsTree = this.mesh.geometry.boundsTree;
		const group = this._group;
		linesGeometry.dispose();
		this.visible = false;
		if ( boundsTree ) {

			// count the number of bounds required
			const targetDepth = this.depth - 1;
			const displayParents = this.displayParents;
			let boundsCount = 0;
			boundsTree.traverse( ( depth, isLeaf ) => {

				if ( depth === targetDepth || isLeaf ) {

					boundsCount ++;
					return true;

				} else if ( displayParents ) {

					boundsCount ++;

				}

			}, group );

			// fill in the position buffer with the bounds corners
			let posIndex = 0;
			const positionArray = new Float32Array( 8 * 3 * boundsCount );
			boundsTree.traverse( ( depth, isLeaf, boundingData ) => {

				const terminate = depth === targetDepth || isLeaf;
				if ( terminate || displayParents ) {

					arrayToBox( boundingData, boundingBox );

					const { min, max } = boundingBox;
					for ( let x = - 1; x <= 1; x += 2 ) {

						const xVal = x < 0 ? min.x : max.x;
						for ( let y = - 1; y <= 1; y += 2 ) {

							const yVal = y < 0 ? min.y : max.y;
							for ( let z = - 1; z <= 1; z += 2 ) {

								const zVal = z < 0 ? min.z : max.z;
								positionArray[ posIndex + 0 ] = xVal;
								positionArray[ posIndex + 1 ] = yVal;
								positionArray[ posIndex + 2 ] = zVal;

								posIndex += 3;

							}

						}

					}

					return terminate;

				}

			}, group );

			let indexArray;
			const indices = this.displayEdges ? edgeIndices : meshIndices;
			if ( positionArray.length > 65535 ) {

				indexArray = new Uint32Array( indices.length * boundsCount );

			} else {

				indexArray = new Uint16Array( indices.length * boundsCount );

			}

			const indexLength = indices.length;
			for ( let i = 0; i < boundsCount; i ++ ) {

				const posOffset = i * 8;
				const indexOffset = i * indexLength;
				for ( let j = 0; j < indexLength; j ++ ) {

					indexArray[ indexOffset + j ] = posOffset + indices[ j ];

				}

			}

			// update the geometry
			linesGeometry.setIndex(
				new BufferAttribute( indexArray, 1, false ),
			);
			linesGeometry.setAttribute(
				'position',
				new BufferAttribute( positionArray, 3, false ),
			);
			this.visible = true;

		}

	}

}

class MeshBVHVisualizer extends Group {

	get color() {

		return this.edgeMaterial.color;

	}

	get opacity() {

		return this.edgeMaterial.opacity;

	}

	set opacity( v ) {

		this.edgeMaterial.opacity = v;
		this.meshMaterial.opacity = v;

	}

	constructor( mesh, depth = 10 ) {

		super();

		this.name = 'MeshBVHVisualizer';
		this.depth = depth;
		this.mesh = mesh;
		this.displayParents = false;
		this.displayEdges = false;
		this._roots = [];

		const edgeMaterial = new LineBasicMaterial( {
			color: 0x00FF88,
			transparent: true,
			opacity: 0.3,
			depthWrite: false,
		} );

		const meshMaterial = new MeshBasicMaterial( {
			color: 0x00FF88,
			transparent: true,
			opacity: 0.3,
			depthWrite: false,
		} );

		meshMaterial.color = edgeMaterial.color;

		this.edgeMaterial = edgeMaterial;
		this.meshMaterial = meshMaterial;

		this.update();

	}

	update() {

		const bvh = this.mesh.geometry.boundsTree;
		const totalRoots = bvh ? bvh._roots.length : 0;
		while ( this._roots.length > totalRoots ) {

			this._roots.pop();

		}

		for ( let i = 0; i < totalRoots; i ++ ) {

			if ( i >= this._roots.length ) {

				const root = new MeshBVHRootVisualizer( this.mesh, this.edgeMaterial, this.depth, i );
				this.add( root );
				this._roots.push( root );

			}

			const root = this._roots[ i ];
			root.depth = this.depth;
			root.mesh = this.mesh;
			root.displayParents = this.displayParents;
			root.displayEdges = this.displayEdges;
			root.material = this.displayEdges ? this.edgeMaterial : this.meshMaterial;
			root.update();

		}

	}

	updateMatrixWorld( ...args ) {

		this.position.copy( this.mesh.position );
		this.rotation.copy( this.mesh.rotation );
		this.scale.copy( this.mesh.scale );

		super.updateMatrixWorld( ...args );

	}

	copy( source ) {

		this.depth = source.depth;
		this.mesh = source.mesh;

	}

	clone() {

		return new MeshBVHVisualizer( this.mesh, this.depth );

	}

	dispose() {

		this.edgeMaterial.dispose();

	}

}

export default MeshBVHVisualizer;
<|endoftext|>


<commit_before>/**
 * echarts图表类：散点图
 *
 * @desc echarts基于Canvas，纯Javascript图表库，提供直观，生动，可交互，可个性化定制的数据统计图表。
 * @author Kener (@Kener-林峰, linzhifeng@baidu.com)
 *
 */
define(function (require) {
    var ComponentBase = require('../component/base');
    var ChartBase = require('./base');

    // 图形依赖
    var SymbolShape = require('../util/shape/Symbol');
    // 组件依赖
    require('../component/axis');
    require('../component/grid');
    require('../component/dataZoom');
    require('../component/dataRange');

    var ecConfig = require('../config');
    var zrUtil = require('zrender/tool/util');
    var zrColor = require('zrender/tool/color');

    /**
     * 构造函数
     * @param {Object} messageCenter echart消息中心
     * @param {ZRender} zr zrender实例
     * @param {Object} series 数据
     * @param {Object} component 组件
     */
    function Scatter(ecTheme, messageCenter, zr, option, myChart){
        // 基类
        ComponentBase.call(this, ecTheme, messageCenter, zr, option, myChart);
        // 图表基类
        ChartBase.call(this);

        this.refresh(option);
    }

    Scatter.prototype = {
        type : ecConfig.CHART_TYPE_SCATTER,
        /**
         * 绘制图形
         */
        _buildShape : function () {
            var series = this.series;
            this._sIndex2ColorMap = {};  // series默认颜色索引，seriesIndex索引到color
            this._symbol = this.option.symbolList;
            this._sIndex2ShapeMap = {};  // series图形类型，seriesIndex索引到_symbol

            this.selectedMap = {};
            this.xMarkMap = {};

            var legend = this.component.legend;
            var seriesArray = [];
            var serie;                              // 临时映射变量
            var serieName;                          // 临时映射变量
            var iconShape;
            var iconType;
            for (var i = 0, l = series.length; i < l; i++) {
                serie = series[i];
                serieName = serie.name;
                if (serie.type == ecConfig.CHART_TYPE_SCATTER) {
                    series[i] = this.reformOption(series[i]);
                    this._sIndex2ShapeMap[i] = this.query(serie, 'symbol')
                                          || this._symbol[i % this._symbol.length];
                    if (legend){
                        this.selectedMap[serieName] = legend.isSelected(serieName);

                        this._sIndex2ColorMap[i] = zrColor.alpha(legend.getColor(serieName), 0.5);

                        iconShape = legend.getItemShape(serieName);
                        if (iconShape) {
                            // 回调legend，换一个更形象的icon
                            var iconType = this._sIndex2ShapeMap[i];
                            iconShape.style.brushType = iconType.match('empty') ? 'stroke' : 'both';
                            iconType = iconType.replace('empty', '').toLowerCase();

                            if (iconType.match('rectangle')) {
                                iconShape.style.x += Math.round(
                                    (iconShape.style.width - iconShape.style.height) / 2
                                );
                                iconShape.style.width = iconShape.style.height;
                            }

                            if (iconType.match('star')) {
                                iconShape.style.n = (iconType.replace('star','') - 0) || 5;
                                iconType = 'star';
                            }

                            if (iconType.match('image')) {
                                iconShape.style.image = iconType.replace(
                                    new RegExp('^image:\\/\\/'), ''
                                );
                                iconShape.style.x += Math.round(
                                    (iconShape.style.width - iconShape.style.height) / 2
                                );
                                iconShape.style.width = iconShape.style.height;
                                iconType = 'image';
                            }

                            iconShape.style.iconType = iconType;
                            legend.setItemShape(serieName, iconShape);
                        }
                    }
                    else {
                        this.selectedMap[serieName] = true;
                        this._sIndex2ColorMap[i] = this.zr.getColor(i);
                    }

                    if (this.selectedMap[serieName]) {
                        seriesArray.push(i);
                    }
                }
            }

            this._buildSeries(seriesArray);

            this.addShapeList();
        },

        /**
         * 构建类目轴为水平方向的散点图系列
         */
        _buildSeries : function (seriesArray) {
            if (seriesArray.length === 0) {
                return;
            }
            var series = this.series;
            var seriesIndex;
            var serie;
            var data;
            var value;
            var xAxis;
            var yAxis;

            var pointList = {};
            var x;
            var y;
            for (var j = 0, k = seriesArray.length; j < k; j++) {
                seriesIndex = seriesArray[j];
                serie = series[seriesIndex];
                if (serie.data.length === 0) {
                    continue;
                }

                xAxis = this.component.xAxis.getAxis(serie.xAxisIndex || 0);
                yAxis = this.component.yAxis.getAxis(serie.yAxisIndex || 0);

                pointList[seriesIndex] = [];
                for (var i = 0, l = serie.data.length; i < l; i++) {
                    data = serie.data[i];
                    value = typeof data != 'undefined'
                            ? (typeof data.value != 'undefined'
                              ? data.value
                              : data)
                            : '-';
                    if (value == '-' || value.length < 2) {
                        // 数据格式不符
                        continue;
                    }
                    x = xAxis.getCoord(value[0]);
                    y = yAxis.getCoord(value[1]);
                    pointList[seriesIndex].push([
                        x,                  // 横坐标
                        y,                  // 纵坐标
                        i,                  // 数据index
                        data.name || ''     // 名称
                    ]);

                }
                this.xMarkMap[seriesIndex] = this._markMap(
                    xAxis, yAxis, serie.data, pointList[seriesIndex]
                );
                this.buildMark(seriesIndex);
            }

            // console.log(pointList)
            this._buildPointList(pointList);
        },

        _markMap : function (xAxis, yAxis, data, pointList) {
            var xMarkMap = {
                min0 : Number.POSITIVE_INFINITY,
                max0 : Number.NEGATIVE_INFINITY,
                sum0 : 0,
                counter0 : 0,
                average0 : 0,
                min1 : Number.POSITIVE_INFINITY,
                max1 : Number.NEGATIVE_INFINITY,
                sum1 : 0,
                counter1 : 0,
                average1 : 0
            };
            var value;
            for (var i = 0, l = pointList.length; i < l; i++) {
                /**
                x,                  // 横坐标
                y,                  // 纵坐标
                i,                  // 数据index
                data.name || ''     // 名称
                 */
                value = data[pointList[i][2]].value || data[pointList[i][2]];
                // 横轴
                if (xMarkMap.min0 > value[0]) {
                    xMarkMap.min0 = value[0];
                    xMarkMap.minY0 = pointList[i][1];
                    xMarkMap.minX0 = pointList[i][0];
                }
                if (xMarkMap.max0 < value[0]) {
                    xMarkMap.max0 = value[0];
                    xMarkMap.maxY0 = pointList[i][1];
                    xMarkMap.maxX0 = pointList[i][0];
                }
                xMarkMap.sum0 += value[0];
                xMarkMap.counter0++;

                // 纵轴
                if (xMarkMap.min1 > value[1]) {
                    xMarkMap.min1 = value[1];
                    xMarkMap.minY1 = pointList[i][1];
                    xMarkMap.minX1 = pointList[i][0];
                }
                if (xMarkMap.max1 < value[1]) {
                    xMarkMap.max1 = value[1];
                    xMarkMap.maxY1 = pointList[i][1];
                    xMarkMap.maxX1 = pointList[i][0];
                }
                xMarkMap.sum1 += value[1];
                xMarkMap.counter1++;
            }

            var gridX = this.component.grid.getX();
            var gridXend = this.component.grid.getXend();
            var gridY = this.component.grid.getY();
            var gridYend = this.component.grid.getYend();

            xMarkMap.average0 = (xMarkMap.sum0 / xMarkMap.counter0).toFixed(2) - 0;
            var x = xAxis.getCoord(xMarkMap.average0);
            // 横轴平均纵向
            xMarkMap.averageLine0 = [
                [x, gridYend],
                [x, gridY]
            ];
            xMarkMap.minLine0 = [
                [xMarkMap.minX0, gridYend],
                [xMarkMap.minX0, gridY]
            ];
            xMarkMap.maxLine0 = [
                [xMarkMap.maxX0, gridYend],
                [xMarkMap.maxX0, gridY]
            ];

            xMarkMap.average1 = (xMarkMap.sum1 / xMarkMap.counter1).toFixed(2) - 0;
            var y = yAxis.getCoord(xMarkMap.average1);
            // 纵轴平均横向
            xMarkMap.averageLine1 = [
                [gridX, y],
                [gridXend, y]
            ];
            xMarkMap.minLine1 = [
                [gridX, xMarkMap.minY1],
                [gridXend, xMarkMap.minY1]
            ];
            xMarkMap.maxLine1 = [
                [gridX, xMarkMap.maxY1],
                [gridXend, xMarkMap.maxY1]
            ];

            return xMarkMap;
        },

        /**
         * 生成折线和折线上的拐点
         */
        _buildPointList : function (pointList) {
            var series = this.series;
            var serie;
            var seriesPL;
            var singlePoint;
            var shape;
            for (var seriesIndex in pointList) {
                serie = series[seriesIndex];
                seriesPL = pointList[seriesIndex];
                if (serie.large && serie.data.length > serie.largeThreshold) {
                    this.shapeList.push(this._getLargeSymbol(
                        seriesPL,
                        this.getItemStyleColor(
                            this.query(
                                serie, 'itemStyle.normal.color'
                            ),
                            seriesIndex,
                            -1
                        ) || this._sIndex2ColorMap[seriesIndex]
                    ));
                    continue;
                }

                /*
                 * pointlist=[
                 *      0  x,
                 *      1  y,
                 *      2  数据index
                 *      3  名称
                 * ]
                 */

                for (var i = 0, l = seriesPL.length; i < l; i++) {
                    singlePoint = seriesPL[i];
                    shape = this._getSymbol(
                        seriesIndex,    // seriesIndex
                        singlePoint[2], // dataIndex
                        singlePoint[3], // name
                        singlePoint[0], // x
                        singlePoint[1]  // y
                    );
                    shape && this.shapeList.push(shape);
                }
            }
            // console.log(this.shapeList)
        },

        /**
         * 生成折线图上的拐点图形
         */
        _getSymbol : function (seriesIndex, dataIndex, name, x, y) {
            var series = this.series;
            var serie = series[seriesIndex];
            var data = serie.data[dataIndex];

            var dataRange = this.component.dataRange;
            var rangColor;
            if (dataRange) {
                rangColor = isNaN(data[2])
                            ? this._sIndex2ColorMap[seriesIndex]
                            : dataRange.getColor(data[2]);
                if (!rangColor) {
                    return null;
                }
            }
            else {
                rangColor = this._sIndex2ColorMap[seriesIndex];
            }

            var itemShape = this.getSymbolShape(
                serie, seriesIndex, data, dataIndex, name,
                x, y,
                this._sIndex2ShapeMap[seriesIndex],
                rangColor,
                'rgba(0,0,0,0)',
                'vertical'
            );
            itemShape.zlevel = this._zlevelBase;
            itemShape._main = true;
            return itemShape;
        },

        _getLargeSymbol : function (pointList, nColor) {
            return new SymbolShape({
                zlevel : this._zlevelBase,
                _main : true,
                hoverable: false,
                style : {
                    pointList : pointList,
                    color : nColor,
                    strokeColor : nColor
                },
                highlightStyle : {
                    pointList : []
                }
            });
        },

        // 位置转换
        getMarkCoord : function (seriesIndex, mpData) {
            var serie = this.series[seriesIndex];
            var xMarkMap = this.xMarkMap[seriesIndex];
            var xAxis = this.component.xAxis.getAxis(serie.xAxisIndex);
            var yAxis = this.component.yAxis.getAxis(serie.yAxisIndex);
            var pos;

            if (mpData.type
                && (mpData.type == 'max' || mpData.type == 'min' || mpData.type == 'average')
            ) {
                // 特殊值内置支持
                // 默认取纵值
                var valueIndex = typeof mpData.valueIndex != 'undefined'
                                 ? mpData.valueIndex : 1;
                pos = [
                    xMarkMap[mpData.type + 'X' + valueIndex],
                    xMarkMap[mpData.type + 'Y' + valueIndex],
                    xMarkMap[mpData.type + 'Line' + valueIndex],
                    xMarkMap[mpData.type + valueIndex]
                ];
            }
            else {
                pos = [
                    typeof mpData.xAxis != 'string' && xAxis.getCoordByIndex
                        ? xAxis.getCoordByIndex(mpData.xAxis || 0)
                        : xAxis.getCoord(mpData.xAxis || 0),

                    typeof mpData.yAxis != 'string' && yAxis.getCoordByIndex
                        ? yAxis.getCoordByIndex(mpData.yAxis || 0)
                        : yAxis.getCoord(mpData.yAxis || 0)
                ];
            }

            return pos;
        },

        /**
         * 刷新
         */
        refresh : function (newOption) {
            if (newOption) {
                this.option = newOption;
                this.series = newOption.series;
            }

            this.backupShapeList();
            this._buildShape();
        },

        /**
         * 值域响应
         * @param {Object} param
         * @param {Object} status
         */
        ondataRange : function (param, status) {
            if (this.component.dataRange) {
                this.refresh();
                status.needRefresh = true;
            }
            return;
        }
    };

    zrUtil.inherits(Scatter, ChartBase);
    zrUtil.inherits(Scatter, ComponentBase);

    // 图表注册
    require('../chart').define('scatter', Scatter);

    return Scatter;
});<commit_msg>【准纪守法，优化代码】代码规范<commit_after>/**
 * echarts图表类：散点图
 *
 * @desc echarts基于Canvas，纯Javascript图表库，提供直观，生动，可交互，可个性化定制的数据统计图表。
 * @author Kener (@Kener-林峰, linzhifeng@baidu.com)
 *
 */
define(function (require) {
    var ComponentBase = require('../component/base');
    var ChartBase = require('./base');

    // 图形依赖
    var SymbolShape = require('../util/shape/Symbol');
    // 组件依赖
    require('../component/axis');
    require('../component/grid');
    require('../component/dataZoom');
    require('../component/dataRange');

    var ecConfig = require('../config');
    var zrUtil = require('zrender/tool/util');
    var zrColor = require('zrender/tool/color');

    /**
     * 构造函数
     * @param {Object} messageCenter echart消息中心
     * @param {ZRender} zr zrender实例
     * @param {Object} series 数据
     * @param {Object} component 组件
     */
    function Scatter(ecTheme, messageCenter, zr, option, myChart){
        // 基类
        ComponentBase.call(this, ecTheme, messageCenter, zr, option, myChart);
        // 图表基类
        ChartBase.call(this);

        this.refresh(option);
    }

    Scatter.prototype = {
        type: ecConfig.CHART_TYPE_SCATTER,
        /**
         * 绘制图形
         */
        _buildShape: function () {
            var series = this.series;
            this._sIndex2ColorMap = {};  // series默认颜色索引，seriesIndex索引到color
            this._symbol = this.option.symbolList;
            this._sIndex2ShapeMap = {};  // series图形类型，seriesIndex索引到_symbol

            this.selectedMap = {};
            this.xMarkMap = {};

            var legend = this.component.legend;
            var seriesArray = [];
            var serie;                              // 临时映射变量
            var serieName;                          // 临时映射变量
            var iconShape;
            var iconType;
            for (var i = 0, l = series.length; i < l; i++) {
                serie = series[i];
                serieName = serie.name;
                if (serie.type === ecConfig.CHART_TYPE_SCATTER) {
                    series[i] = this.reformOption(series[i]);
                    this._sIndex2ShapeMap[i] = this.query(serie, 'symbol')
                                          || this._symbol[i % this._symbol.length];
                    if (legend){
                        this.selectedMap[serieName] = legend.isSelected(serieName);

                        this._sIndex2ColorMap[i] = zrColor.alpha(legend.getColor(serieName), 0.5);

                        iconShape = legend.getItemShape(serieName);
                        if (iconShape) {
                            // 回调legend，换一个更形象的icon
                            var iconType = this._sIndex2ShapeMap[i];
                            iconShape.style.brushType = iconType.match('empty') ? 'stroke' : 'both';
                            iconType = iconType.replace('empty', '').toLowerCase();

                            if (iconType.match('rectangle')) {
                                iconShape.style.x += Math.round(
                                    (iconShape.style.width - iconShape.style.height) / 2
                                );
                                iconShape.style.width = iconShape.style.height;
                            }

                            if (iconType.match('star')) {
                                iconShape.style.n = (iconType.replace('star','') - 0) || 5;
                                iconType = 'star';
                            }

                            if (iconType.match('image')) {
                                iconShape.style.image = iconType.replace(
                                    new RegExp('^image:\\/\\/'), ''
                                );
                                iconShape.style.x += Math.round(
                                    (iconShape.style.width - iconShape.style.height) / 2
                                );
                                iconShape.style.width = iconShape.style.height;
                                iconType = 'image';
                            }

                            iconShape.style.iconType = iconType;
                            legend.setItemShape(serieName, iconShape);
                        }
                    }
                    else {
                        this.selectedMap[serieName] = true;
                        this._sIndex2ColorMap[i] = this.zr.getColor(i);
                    }

                    if (this.selectedMap[serieName]) {
                        seriesArray.push(i);
                    }
                }
            }

            this._buildSeries(seriesArray);

            this.addShapeList();
        },

        /**
         * 构建类目轴为水平方向的散点图系列
         */
        _buildSeries: function (seriesArray) {
            if (seriesArray.length === 0) {
                return;
            }
            var series = this.series;
            var seriesIndex;
            var serie;
            var data;
            var value;
            var xAxis;
            var yAxis;

            var pointList = {};
            var x;
            var y;
            for (var j = 0, k = seriesArray.length; j < k; j++) {
                seriesIndex = seriesArray[j];
                serie = series[seriesIndex];
                if (serie.data.length === 0) {
                    continue;
                }

                xAxis = this.component.xAxis.getAxis(serie.xAxisIndex || 0);
                yAxis = this.component.yAxis.getAxis(serie.yAxisIndex || 0);

                pointList[seriesIndex] = [];
                for (var i = 0, l = serie.data.length; i < l; i++) {
                    data = serie.data[i];
                    value = data != null
                            ? (data.value != null
                              ? data.value
                              : data)
                            : '-';
                    if (value === '-' || value.length < 2) {
                        // 数据格式不符
                        continue;
                    }
                    x = xAxis.getCoord(value[0]);
                    y = yAxis.getCoord(value[1]);
                    pointList[seriesIndex].push([
                        x,                  // 横坐标
                        y,                  // 纵坐标
                        i,                  // 数据index
                        data.name || ''     // 名称
                    ]);

                }
                this.xMarkMap[seriesIndex] = this._markMap(
                    xAxis, yAxis, serie.data, pointList[seriesIndex]
                );
                this.buildMark(seriesIndex);
            }

            // console.log(pointList)
            this._buildPointList(pointList);
        },

        _markMap: function (xAxis, yAxis, data, pointList) {
            var xMarkMap = {
                min0: Number.POSITIVE_INFINITY,
                max0: Number.NEGATIVE_INFINITY,
                sum0: 0,
                counter0: 0,
                average0: 0,
                min1: Number.POSITIVE_INFINITY,
                max1: Number.NEGATIVE_INFINITY,
                sum1: 0,
                counter1: 0,
                average1: 0
            };
            var value;
            for (var i = 0, l = pointList.length; i < l; i++) {
                /**
                x,                  // 横坐标
                y,                  // 纵坐标
                i,                  // 数据index
                data.name || ''     // 名称
                 */
                value = data[pointList[i][2]].value || data[pointList[i][2]];
                // 横轴
                if (xMarkMap.min0 > value[0]) {
                    xMarkMap.min0 = value[0];
                    xMarkMap.minY0 = pointList[i][1];
                    xMarkMap.minX0 = pointList[i][0];
                }
                if (xMarkMap.max0 < value[0]) {
                    xMarkMap.max0 = value[0];
                    xMarkMap.maxY0 = pointList[i][1];
                    xMarkMap.maxX0 = pointList[i][0];
                }
                xMarkMap.sum0 += value[0];
                xMarkMap.counter0++;

                // 纵轴
                if (xMarkMap.min1 > value[1]) {
                    xMarkMap.min1 = value[1];
                    xMarkMap.minY1 = pointList[i][1];
                    xMarkMap.minX1 = pointList[i][0];
                }
                if (xMarkMap.max1 < value[1]) {
                    xMarkMap.max1 = value[1];
                    xMarkMap.maxY1 = pointList[i][1];
                    xMarkMap.maxX1 = pointList[i][0];
                }
                xMarkMap.sum1 += value[1];
                xMarkMap.counter1++;
            }

            var gridX = this.component.grid.getX();
            var gridXend = this.component.grid.getXend();
            var gridY = this.component.grid.getY();
            var gridYend = this.component.grid.getYend();

            xMarkMap.average0 = (xMarkMap.sum0 / xMarkMap.counter0).toFixed(2) - 0;
            var x = xAxis.getCoord(xMarkMap.average0);
            // 横轴平均纵向
            xMarkMap.averageLine0 = [
                [x, gridYend],
                [x, gridY]
            ];
            xMarkMap.minLine0 = [
                [xMarkMap.minX0, gridYend],
                [xMarkMap.minX0, gridY]
            ];
            xMarkMap.maxLine0 = [
                [xMarkMap.maxX0, gridYend],
                [xMarkMap.maxX0, gridY]
            ];

            xMarkMap.average1 = (xMarkMap.sum1 / xMarkMap.counter1).toFixed(2) - 0;
            var y = yAxis.getCoord(xMarkMap.average1);
            // 纵轴平均横向
            xMarkMap.averageLine1 = [
                [gridX, y],
                [gridXend, y]
            ];
            xMarkMap.minLine1 = [
                [gridX, xMarkMap.minY1],
                [gridXend, xMarkMap.minY1]
            ];
            xMarkMap.maxLine1 = [
                [gridX, xMarkMap.maxY1],
                [gridXend, xMarkMap.maxY1]
            ];

            return xMarkMap;
        },

        /**
         * 生成折线和折线上的拐点
         */
        _buildPointList: function (pointList) {
            var series = this.series;
            var serie;
            var seriesPL;
            var singlePoint;
            var shape;
            for (var seriesIndex in pointList) {
                serie = series[seriesIndex];
                seriesPL = pointList[seriesIndex];
                if (serie.large && serie.data.length > serie.largeThreshold) {
                    this.shapeList.push(this._getLargeSymbol(
                        seriesPL,
                        this.getItemStyleColor(
                            this.query(
                                serie, 'itemStyle.normal.color'
                            ),
                            seriesIndex,
                            -1
                        ) || this._sIndex2ColorMap[seriesIndex]
                    ));
                    continue;
                }

                /*
                 * pointlist=[
                 *      0  x,
                 *      1  y,
                 *      2  数据index
                 *      3  名称
                 * ]
                 */

                for (var i = 0, l = seriesPL.length; i < l; i++) {
                    singlePoint = seriesPL[i];
                    shape = this._getSymbol(
                        seriesIndex,    // seriesIndex
                        singlePoint[2], // dataIndex
                        singlePoint[3], // name
                        singlePoint[0], // x
                        singlePoint[1]  // y
                    );
                    shape && this.shapeList.push(shape);
                }
            }
            // console.log(this.shapeList)
        },

        /**
         * 生成折线图上的拐点图形
         */
        _getSymbol: function (seriesIndex, dataIndex, name, x, y) {
            var series = this.series;
            var serie = series[seriesIndex];
            var data = serie.data[dataIndex];

            var dataRange = this.component.dataRange;
            var rangColor;
            if (dataRange) {
                rangColor = isNaN(data[2])
                            ? this._sIndex2ColorMap[seriesIndex]
                            : dataRange.getColor(data[2]);
                if (!rangColor) {
                    return null;
                }
            }
            else {
                rangColor = this._sIndex2ColorMap[seriesIndex];
            }

            var itemShape = this.getSymbolShape(
                serie, seriesIndex, data, dataIndex, name,
                x, y,
                this._sIndex2ShapeMap[seriesIndex],
                rangColor,
                'rgba(0,0,0,0)',
                'vertical'
            );
            itemShape.zlevel = this._zlevelBase;
            itemShape._main = true;
            return itemShape;
        },

        _getLargeSymbol: function (pointList, nColor) {
            return new SymbolShape({
                zlevel: this._zlevelBase,
                _main: true,
                hoverable: false,
                style: {
                    pointList: pointList,
                    color: nColor,
                    strokeColor: nColor
                },
                highlightStyle: {
                    pointList: [ ]
                }
            });
        },

        // 位置转换
        getMarkCoord: function (seriesIndex, mpData) {
            var serie = this.series[seriesIndex];
            var xMarkMap = this.xMarkMap[seriesIndex];
            var xAxis = this.component.xAxis.getAxis(serie.xAxisIndex);
            var yAxis = this.component.yAxis.getAxis(serie.yAxisIndex);
            var pos;

            if (mpData.type
                && (mpData.type === 'max' || mpData.type === 'min' || mpData.type === 'average')
            ) {
                // 特殊值内置支持
                // 默认取纵值
                var valueIndex = mpData.valueIndex != null ? mpData.valueIndex : 1;
                pos = [
                    xMarkMap[mpData.type + 'X' + valueIndex],
                    xMarkMap[mpData.type + 'Y' + valueIndex],
                    xMarkMap[mpData.type + 'Line' + valueIndex],
                    xMarkMap[mpData.type + valueIndex]
                ];
            }
            else {
                pos = [
                    typeof mpData.xAxis != 'string' && xAxis.getCoordByIndex
                        ? xAxis.getCoordByIndex(mpData.xAxis || 0)
                        : xAxis.getCoord(mpData.xAxis || 0),

                    typeof mpData.yAxis != 'string' && yAxis.getCoordByIndex
                        ? yAxis.getCoordByIndex(mpData.yAxis || 0)
                        : yAxis.getCoord(mpData.yAxis || 0)
                ];
            }

            return pos;
        },

        /**
         * 刷新
         */
        refresh: function (newOption) {
            if (newOption) {
                this.option = newOption;
                this.series = newOption.series;
            }

            this.backupShapeList();
            this._buildShape();
        },

        /**
         * 值域响应
         * @param {Object} param
         * @param {Object} status
         */
        ondataRange: function (param, status) {
            if (this.component.dataRange) {
                this.refresh();
                status.needRefresh = true;
            }
            return;
        }
    };

    zrUtil.inherits(Scatter, ChartBase);
    zrUtil.inherits(Scatter, ComponentBase);

    // 图表注册
    require('../chart').define('scatter', Scatter);

    return Scatter;
});<|endoftext|>


<commit_before>/**
 * React Starter Kit (https://www.reactstarterkit.com/)
 *
 * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

import 'babel-polyfill';
import ReactDOM from 'react-dom';
import FastClick from 'fastclick';
import { match } from 'universal-router';
import routes from './routes';
import history from './core/history';
import { addEventListener, removeEventListener } from './core/DOMUtils';

const context = {
  insertCss: styles => styles._insertCss(),
  setTitle: value => (document.title = value),
  setMeta: (name, content) => {
    // Remove and create a new <meta /> tag in order to make it work
    // with bookmarks in Safari
    const elements = document.getElementsByTagName('meta');
    Array.from(elements).forEach((element) => {
      if (element.getAttribute('name') === name) {
        element.parentNode.removeChild(element);
      }
    });
    const meta = document.createElement('meta');
    meta.setAttribute('name', name);
    meta.setAttribute('content', content);
    document
      .getElementsByTagName('head')[0]
      .appendChild(meta);
  },
};

// Restore the scroll position if it was saved into the state
function restoreScrollPosition(state) {
  if (state && state.scrollY !== undefined) {
    window.scrollTo(state.scrollX, state.scrollY);
  } else {
    window.scrollTo(0, 0);
  }
}

let renderComplete = (state, callback) => {
  const elem = document.getElementById('css');
  if (elem) elem.parentNode.removeChild(elem);
  callback(true);
  renderComplete = (s) => {
    restoreScrollPosition(s);

    // Google Analytics tracking. Don't send 'pageview' event after
    // the initial rendering, as it was already sent
    window.ga('send', 'pageview');

    callback(true);
  };
};

function render(container, state, component) {
  return new Promise((resolve, reject) => {
    try {
      ReactDOM.render(
        component,
        container,
        renderComplete.bind(undefined, state, resolve)
      );
    } catch (err) {
      reject(err);
    }
  });
}

function run() {
  let currentLocation = null;
  const container = document.getElementById('app');

  // Make taps on links and buttons work fast on mobiles
  FastClick.attach(document.body);

  // Re-render the app when window.location changes
  const removeLocationListener = history.listen(location => {
    currentLocation = location;
    match(routes, {
      path: location.pathname,
      query: location.query,
      state: location.state,
      context,
      render: render.bind(undefined, container, location.state),
    }).catch(err => console.error(err)); // eslint-disable-line no-console
  });

  // Save the page scroll position into the current location's state
  const supportPageOffset = window.pageXOffset !== undefined;
  const isCSS1Compat = ((document.compatMode || '') === 'CSS1Compat');
  const setPageOffset = () => {
    currentLocation.state = currentLocation.state || Object.create(null);
    if (supportPageOffset) {
      currentLocation.state.scrollX = window.pageXOffset;
      currentLocation.state.scrollY = window.pageYOffset;
    } else {
      currentLocation.state.scrollX = isCSS1Compat ?
        document.documentElement.scrollLeft : document.body.scrollLeft;
      currentLocation.state.scrollY = isCSS1Compat ?
        document.documentElement.scrollTop : document.body.scrollTop;
    }
  };

  addEventListener(window, 'scroll', setPageOffset);
  addEventListener(window, 'pagehide', () => {
    removeEventListener(window, 'scroll', setPageOffset);
    removeLocationListener();
  });
}

// Run the application when both DOM is ready and page content is loaded
if (['complete', 'loaded', 'interactive'].includes(document.readyState) && document.body) {
  run();
} else {
  document.addEventListener('DOMContentLoaded', run, false);
}
<commit_msg>removeLocationListener -> removeHistoryListener<commit_after>/**
 * React Starter Kit (https://www.reactstarterkit.com/)
 *
 * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

import 'babel-polyfill';
import ReactDOM from 'react-dom';
import FastClick from 'fastclick';
import { match } from 'universal-router';
import routes from './routes';
import history from './core/history';
import { addEventListener, removeEventListener } from './core/DOMUtils';

const context = {
  insertCss: styles => styles._insertCss(),
  setTitle: value => (document.title = value),
  setMeta: (name, content) => {
    // Remove and create a new <meta /> tag in order to make it work
    // with bookmarks in Safari
    const elements = document.getElementsByTagName('meta');
    Array.from(elements).forEach((element) => {
      if (element.getAttribute('name') === name) {
        element.parentNode.removeChild(element);
      }
    });
    const meta = document.createElement('meta');
    meta.setAttribute('name', name);
    meta.setAttribute('content', content);
    document
      .getElementsByTagName('head')[0]
      .appendChild(meta);
  },
};

// Restore the scroll position if it was saved into the state
function restoreScrollPosition(state) {
  if (state && state.scrollY !== undefined) {
    window.scrollTo(state.scrollX, state.scrollY);
  } else {
    window.scrollTo(0, 0);
  }
}

let renderComplete = (state, callback) => {
  const elem = document.getElementById('css');
  if (elem) elem.parentNode.removeChild(elem);
  callback(true);
  renderComplete = (s) => {
    restoreScrollPosition(s);

    // Google Analytics tracking. Don't send 'pageview' event after
    // the initial rendering, as it was already sent
    window.ga('send', 'pageview');

    callback(true);
  };
};

function render(container, state, component) {
  return new Promise((resolve, reject) => {
    try {
      ReactDOM.render(
        component,
        container,
        renderComplete.bind(undefined, state, resolve)
      );
    } catch (err) {
      reject(err);
    }
  });
}

function run() {
  let currentLocation = null;
  const container = document.getElementById('app');

  // Make taps on links and buttons work fast on mobiles
  FastClick.attach(document.body);

  // Re-render the app when window.location changes
  const removeHistoryListener = history.listen(location => {
    currentLocation = location;
    match(routes, {
      path: location.pathname,
      query: location.query,
      state: location.state,
      context,
      render: render.bind(undefined, container, location.state),
    }).catch(err => console.error(err)); // eslint-disable-line no-console
  });

  // Save the page scroll position into the current location's state
  const supportPageOffset = window.pageXOffset !== undefined;
  const isCSS1Compat = ((document.compatMode || '') === 'CSS1Compat');
  const setPageOffset = () => {
    currentLocation.state = currentLocation.state || Object.create(null);
    if (supportPageOffset) {
      currentLocation.state.scrollX = window.pageXOffset;
      currentLocation.state.scrollY = window.pageYOffset;
    } else {
      currentLocation.state.scrollX = isCSS1Compat ?
        document.documentElement.scrollLeft : document.body.scrollLeft;
      currentLocation.state.scrollY = isCSS1Compat ?
        document.documentElement.scrollTop : document.body.scrollTop;
    }
  };

  addEventListener(window, 'scroll', setPageOffset);
  addEventListener(window, 'pagehide', () => {
    removeEventListener(window, 'scroll', setPageOffset);
    removeHistoryListener();
  });
}

// Run the application when both DOM is ready and page content is loaded
if (['complete', 'loaded', 'interactive'].includes(document.readyState) && document.body) {
  run();
} else {
  document.addEventListener('DOMContentLoaded', run, false);
}
<|endoftext|>


<commit_before>import 'babel-polyfill'
import 'normalize.css'
import React from 'react'
import ReactDOM from 'react-dom'
import ReactGA from 'react-ga'
import Router from 'react-router/lib/Router'
import browserHistory from 'react-router/lib/browserHistory'
import configureStore from './store/configure-store'
import createRoutes from './routes'
import match from 'react-router/lib/match'
import { Provider } from 'react-redux'
import { syncHistoryWithStore } from 'react-router-redux'

let reduxState
if (window.__REDUX_STATE__) {
  reduxState = window.__REDUX_STATE__
}

configureStore(browserHistory, reduxState)
  .then((store) => {
    const history = syncHistoryWithStore(browserHistory, store)

    const routes = createRoutes(history)

    function scrollToTopAndFirePageview() {
      if(window) {
        window.scrollTo(0, 0)
        // send Google Analytics Pageview event on router changed
        ReactGA.pageview(window.location.pathname)
      }
    }

    // calling `match` is simply for side effects of
    // loading route/component code for the initial location
    // https://github.com/ReactTraining/react-router/blob/v3/docs/guides/ServerRendering.md#async-routes
    match({ history, routes }, (error, redirectLocation, renderProps) => {
      if (typeof window !== 'undefined') {
        // add Google Analytics
        ReactGA.initialize('UA-69336956-1')
        ReactGA.set({ page: window.location.pathname })
      }
      ReactDOM.hydrate((
        <Provider store={store}>
          <Router {...renderProps} onUpdate={scrollToTopAndFirePageview}/>
        </Provider>
      ), document.getElementById('root'))
    })
  })

<commit_msg>Move function declaration out of promise chain<commit_after>import 'babel-polyfill'
import 'normalize.css'
import React from 'react'
import ReactDOM from 'react-dom'
import ReactGA from 'react-ga'
import Router from 'react-router/lib/Router'
import browserHistory from 'react-router/lib/browserHistory'
import configureStore from './store/configure-store'
import createRoutes from './routes'
import match from 'react-router/lib/match'
import { Provider } from 'react-redux'
import { syncHistoryWithStore } from 'react-router-redux'

let reduxState
if (window.__REDUX_STATE__) {
  reduxState = window.__REDUX_STATE__
}

function scrollToTopAndFirePageview() {
  if(window) {
    window.scrollTo(0, 0)
    // send Google Analytics Pageview event on router changed
    ReactGA.pageview(window.location.pathname)
  }
}

configureStore(browserHistory, reduxState)
  .then((store) => {
    const history = syncHistoryWithStore(browserHistory, store)
    const routes = createRoutes(history)

    // calling `match` is simply for side effects of
    // loading route/component code for the initial location
    // https://github.com/ReactTraining/react-router/blob/v3/docs/guides/ServerRendering.md#async-routes
    match({ history, routes }, (error, redirectLocation, renderProps) => {
      if (typeof window !== 'undefined') {
        // add Google Analytics
        ReactGA.initialize('UA-69336956-1')
        ReactGA.set({ page: window.location.pathname })
      }
      ReactDOM.hydrate((
        <Provider store={store}>
          <Router {...renderProps} onUpdate={scrollToTopAndFirePageview}/>
        </Provider>
      ), document.getElementById('root'))
    })
  })

<|endoftext|>


<commit_before>import 'babel-polyfill';
import { trigger } from 'redial';

import React from 'react';
import { render } from 'react-dom';
import Router from 'react-router/lib/Router';
import match from 'react-router/lib/match';
import browserHistory from 'react-router/lib/browserHistory';
import { Provider } from 'react-redux';
import { StyleSheet } from 'aphrodite';

// Your app's reducer and routes:
import createRoutes from './routes/root';
import { configureStore } from './store';

const initialState = window.INITIAL_STATE || {};

// Set up Redux (note: this API requires redux@>=3.1.0):
const store = configureStore(initialState);
const { dispatch } = store;
const { pathname, search, hash } = window.location;
const location = `${pathname}${search}${hash}`;
const routes = createRoutes(store);
const container = document.getElementById('root');
StyleSheet.rehydrate(window.renderedClassNames);

browserHistory.listen(location => {
  // Match routes based on location object:
  match({ routes, location }, (error, redirectLocation, renderProps) => {
    // Get array of route handler components:
    const { components } = renderProps;

    // Define locals to be provided to all lifecycle hooks:
    const locals = {
        path: renderProps.location.pathname,
        query: renderProps.location.query,
        params: renderProps.params,

        // Allow lifecycle hooks to dispatch Redux actions:
        dispatch,
      };

    // Don't fetch data for initial route, server has already done the work:
    if (window.INITIAL_STATE) {
      // Delete initial data so that subsequent data fetches can occur:
      delete window.INITIAL_STATE;
    } else {
      // Fetch mandatory data dependencies for 2nd route change onwards:
      trigger('fetch', components, locals);
    }

    // Fetch deferred, client-only data dependencies:
    trigger('defer', components, locals);
  });
});

// Render app with Redux and router context to container element:
render((
  <Provider store={store}>
      <Router history={browserHistory} routes={routes} />
  </Provider>
), container);
<commit_msg>Fix warning Warning: React attempted to reuse markup in a container but the checksum was invalid. When directly visit post pages<commit_after>import 'babel-polyfill';
import { trigger } from 'redial';

import React from 'react';
import { render } from 'react-dom';
import Router from 'react-router/lib/Router';
import match from 'react-router/lib/match';
import browserHistory from 'react-router/lib/browserHistory';
import { Provider } from 'react-redux';
import { StyleSheet } from 'aphrodite';

// Your app's reducer and routes:
import createRoutes from './routes/root';
import { configureStore } from './store';

const initialState = window.INITIAL_STATE || {};

// Set up Redux (note: this API requires redux@>=3.1.0):
const store = configureStore(initialState);
const { dispatch } = store;
const { pathname, search, hash } = window.location;
const location = `${pathname}${search}${hash}`;
const routes = createRoutes(store);
const container = document.getElementById('root');
StyleSheet.rehydrate(window.renderedClassNames);

browserHistory.listen(location => {
  // Match routes based on location object:
  match({ routes, location }, (error, redirectLocation, renderProps) => {
    // Get array of route handler components:
    const { components } = renderProps;

    // Define locals to be provided to all lifecycle hooks:
    const locals = {
        path: renderProps.location.pathname,
        query: renderProps.location.query,
        params: renderProps.params,

        // Allow lifecycle hooks to dispatch Redux actions:
        dispatch,
      };

    // Don't fetch data for initial route, server has already done the work:
    if (window.INITIAL_STATE) {
      // Delete initial data so that subsequent data fetches can occur:
      delete window.INITIAL_STATE;
    } else {
      // Fetch mandatory data dependencies for 2nd route change onwards:
      trigger('fetch', components, locals);
    }

    // Fetch deferred, client-only data dependencies:
    trigger('defer', components, locals);

    // Render app with Redux and router context to container element:
    render((
      <Provider store={store}>
          <Router history={browserHistory} routes={routes} />
      </Provider>
    ), container);
  });
});
<|endoftext|>


<commit_before>'use strict'

var express = require('express');
var bodyParser = require('body-parser');
var request = require('request');
var Q = require('q');
var request = require('request');

var utilities = require('./utilities');
var factory = require('./factory');
var messenger = require('../bots/messenger.bot');

/**
 * Main entry point for the bot server
 *
 * @param botHandlers
 * @param actionHandlers
 * @param config
 */
function start(botHandlers, actionHandlers, config) {
  var port = process.env.PORT || 3000;
  var app = express();

  console.log('Starting server');

  addMiddleware(app);

  // remove these
  // addTestRoutes(app);

  // all bots go to this route
  app.post('/:bot', handleRequest(botHandlers, actionHandlers, config));

  // look up Express - app.all - figure out how to process FBM verification
    // should be able to see if it's a Get or Post

  // FB Messenger verification route
  app.get('/messenger', function(req, res) {
    messenger.verify(req, res);
  });

  app.listen(port, function () {
    console.log('API listening for bots on port ' + port);
  });
}

/**
 * Add Express middleware
 *
 * @param app The express server
 */
function addMiddleware(app) {
  app.use(bodyParser.urlencoded({ extended: true }));
  app.use(bodyParser.json());
}

/**
 * Add test routes (remove in the future)
 *
 * @param app The express server
 */
// function addTestRoutes(app) {

//   // test route
//   app.get('/', function (req, res) {
//     res.status(200).send('Hello world!')
//   });

//   // Slack hellobot - keep for testing
//   app.post('/hello', require('../deprecated/hellobot.js'));
//   // Slack dicebot - keep for testing
//   app.post('/roll', require('../deprecated/dicebot.js'));
// }

/**
 * Handle requests from the bots to our api
 *
 * @param botHandlers
 * @param actionHandlers
 * @param config
 * @returns {Function}
 */
function handleRequest(botHandlers, actionHandlers, config) {
  return function (req, res) {
    var context = getContext(req, res, config);
    console.log('Context captured from request: ' + JSON.stringify(context));
    var botHandler = factory.getBotHandler(botHandlers, context);
    var genericRequests = botHandler.translateRequestToGenericFormats(context);
    genericRequests.forEach((genericRequest) => {
      var actionHandler = factory.getActionHandler(actionHandlers, genericRequest);
      try {
        actionHandler.processRequest(genericRequest)
            .then(function (genericResponse) {
              // console.log("Generic Response returned in Server: " + genericResponse);
              var payloads = botHandler.generateResponsePayloads(genericResponse);
              // console.log("Payloads generated: " + JSON.stringify(payloads));
              return sendResponses(context, payloads);
            })
            .done();
      }
      catch(error) {
        console.log('Catching an error in Try/Catch in server: ' + error);
      }
    });

  }
}

/**
 * Lines up payloads and sequentially calls Send for each
 *
 * @param context
 * @param payloads
 * @returns {Promise}
 */
function sendResponses(context, payloads) {
  // cut off function if no payloads
  if (!payloads) {
    return null
  };
  // force payloads into an array (is this necessary?)
  payloads = [].concat(payloads || []);
  var calls = payloads.map(function (payload) {
    return function () {
      return sendResponseToPlatform(context, payload)
          .catch(function (err) {
            console.log('Error from send to platform: ' + err);
          });
    }
  });
  return utilities.chainPromises(calls);
}

/**
 * Checks to see if payload should actually be sent to a platform
 * If so, passes it to Send fuction
 *
 * @param context
 * @param payload
 * @return {sendRequestAsReply}
 */
function sendResponseToPlatform(context, payload) {
  if (context.isTest) {
    console.log("Test flag detected in payload context.");
    context.sendResponse(payload);
    return Q.when();
  }
  // if this part needed?
  else if (!payload || (payload === {})) {
    console.log("No payload data detected.");
    return Q.when();
  }
  else {
    return sendRequestAsReply(payload, context);
  }
}

/**
 * Makes the request to send to payload
 *
 * @param context
 * @param payload
 * @return {Promise}
 */
function sendRequestAsReply(payload, context) {
  var deferred = Q.defer();
  console.log("Last step before sending this payload: " + JSON.stringify(payload));
  request(payload, function (error, response, body) {
    if (error) {
      console.log("Ran into error while making request to send Slack payload: " + error);
      deferred.reject(error);
    }
    else {
      deferred.resolve();
    }
  });
  return deferred.promise;
}

/**
 * Create a generic context object from the Express
 * request and response objects
 *
 * @param req
 * @param res
 * @param config
 * @returns {context}
 */
function getContext(req, res, config) {
  return {
    config: config,
    userRequest: req.body,
    isTest: !!req.params.isTest,
    bot: req.params.bot,
    sendResponse: function (payload) {
      res.send(payload);
    },
    finishResponse: function() {
      res.status(200).end();
    }
  };
}

module.exports = {
  start: start,
  addMiddleware: addMiddleware,
  // addTestRoutes: addTestRoutes,
  handleRequest: handleRequest,
  sendResponses: sendResponses,
  sendResponseToPlatform: sendResponseToPlatform,
  sendRequestAsReply: sendRequestAsReply,
  getContext: getContext
};
<commit_msg>Change server to app.all<commit_after>'use strict'

var express = require('express');
var bodyParser = require('body-parser');
var request = require('request');
var Q = require('q');
var request = require('request');

var utilities = require('./utilities');
var factory = require('./factory');
var messenger = require('../bots/messenger.bot');

/**
 * Main entry point for the bot server
 *
 * @param botHandlers
 * @param actionHandlers
 * @param config
 */
function start(botHandlers, actionHandlers, config) {
  var port = process.env.PORT || 3000;
  var app = express();

  console.log('Starting server');

  addMiddleware(app);

  // remove these
  // addTestRoutes(app);

  // all bots go to this route
  // app.post('/:bot', handleRequest(botHandlers, actionHandlers, config));
  app.all('/:bot', handleRequest(botHandlers, actionHandlers, config));

  // look up Express - app.all - figure out how to process FBM verification
    // should be able to see if it's a Get or Post

  // FB Messenger verification route
  // app.get('/messenger', function(req, res) {
  //   messenger.verify(req, res);
  // });

  app.listen(port, function () {
    console.log('API listening for bots on port ' + port);
  });
}

/**
 * Add Express middleware
 *
 * @param app The express server
 */
function addMiddleware(app) {
  app.use(bodyParser.urlencoded({ extended: true }));
  app.use(bodyParser.json());
}

/**
 * Add test routes (remove in the future)
 *
 * @param app The express server
 */
// function addTestRoutes(app) {

//   // test route
//   app.get('/', function (req, res) {
//     res.status(200).send('Hello world!')
//   });

//   // Slack hellobot - keep for testing
//   app.post('/hello', require('../deprecated/hellobot.js'));
//   // Slack dicebot - keep for testing
//   app.post('/roll', require('../deprecated/dicebot.js'));
// }

/**
 * Handle requests from the bots to our api
 *
 * @param botHandlers
 * @param actionHandlers
 * @param config
 * @returns {Function}
 */
function handleRequest(botHandlers, actionHandlers, config) {
  return function (req, res) {
    var context = getContext(req, res, config);
    console.log('Context captured from request: ' + JSON.stringify(context));
    var botHandler = factory.getBotHandler(botHandlers, context);
    var genericRequests = botHandler.translateRequestToGenericFormats(context);
    genericRequests.forEach((genericRequest) => {
      var actionHandler = factory.getActionHandler(actionHandlers, genericRequest);
      try {
        actionHandler.processRequest(genericRequest)
            .then(function (genericResponse) {
              // console.log("Generic Response returned in Server: " + genericResponse);
              var payloads = botHandler.generateResponsePayloads(genericResponse);
              // console.log("Payloads generated: " + JSON.stringify(payloads));
              return sendResponses(context, payloads);
            })
            .done();
      }
      catch(error) {
        console.log('Catching an error in Try/Catch in server: ' + error);
      }
    });

  }
}

/**
 * Lines up payloads and sequentially calls Send for each
 *
 * @param context
 * @param payloads
 * @returns {Promise}
 */
function sendResponses(context, payloads) {
  // cut off function if no payloads
  if (!payloads) {
    return null
  };
  // force payloads into an array (is this necessary?)
  payloads = [].concat(payloads || []);
  var calls = payloads.map(function (payload) {
    return function () {
      return sendResponseToPlatform(context, payload)
          .catch(function (err) {
            console.log('Error from send to platform: ' + err);
          });
    }
  });
  return utilities.chainPromises(calls);
}

/**
 * Checks to see if payload should actually be sent to a platform
 * If so, passes it to Send fuction
 *
 * @param context
 * @param payload
 * @return {sendRequestAsReply}
 */
function sendResponseToPlatform(context, payload) {
  if (context.isTest) {
    console.log("Test flag detected in payload context.");
    context.sendResponse(payload);
    return Q.when();
  }
  // if this part needed?
  else if (!payload || (payload === {})) {
    console.log("No payload data detected.");
    return Q.when();
  }
  else {
    return sendRequestAsReply(payload, context);
  }
}

/**
 * Makes the request to send to payload
 *
 * @param context
 * @param payload
 * @return {Promise}
 */
function sendRequestAsReply(payload, context) {
  var deferred = Q.defer();
  console.log("Last step before sending this payload: " + JSON.stringify(payload));
  request(payload, function (error, response, body) {
    if (error) {
      console.log("Ran into error while making request to send Slack payload: " + error);
      deferred.reject(error);
    }
    else {
      deferred.resolve();
    }
  });
  return deferred.promise;
}

/**
 * Create a generic context object from the Express
 * request and response objects
 *
 * @param req
 * @param res
 * @param config
 * @returns {context}
 */
function getContext(req, res, config) {
  return {
    config: config,
    userRequest: req.body,
    isTest: !!req.params.isTest,
    bot: req.params.bot,
    sendResponse: function (payload) {
      res.send(payload);
    },
    finishResponse: function() {
      res.status(200).end();
    }
  };
}

module.exports = {
  start: start,
  addMiddleware: addMiddleware,
  // addTestRoutes: addTestRoutes,
  handleRequest: handleRequest,
  sendResponses: sendResponses,
  sendResponseToPlatform: sendResponseToPlatform,
  sendRequestAsReply: sendRequestAsReply,
  getContext: getContext
};
<|endoftext|>


<commit_before>const Events = require('events');

// Here we have a copy of the site in a CDN close to the user. Ensure the
// CDN is configured to not forward query strings to reduce latency.
const URL_ = '//d18ks85av1x0pi.cloudfront.net/scripts/blank.js?nocache';

class Script {
  constructor(url) {
    this._element = document.createElement('script');
    this._element.src = url;
    this.loaded = new Promise((resolve, reject) => {
      this._element.onload = resolve;
      this._element.onerror = reject;
    });
    document.head.appendChild(this._element);
  }

  remove() {
    if (!this._element) return;
    this._element.parentNode.removeChild(this._element);
    this._element = null;
  }
}

class Request {
  constructor() {
    this._script = new Script(`${URL_}&v=${Date.now()}`);
    this._script.loaded.then(() => this._script.remove(), () => this._script.remove());
  }

  abort() {
    this._script.remove();
  }

  get loaded() {
    return this._script.loaded;
  }
}

module.exports = class Ping {
  constructor() {
    this.events = new Events();
    this.done = false; // Whether it has finished (whether succesfully or not)
    this.failed = false; // Whether it finished failing
    this._send();
  }

  get lag() {
    return (this.end || Date.now()) - this.start;
  }

  /**
   * Cancels the ping
   */
  abort() {
    if (this.done) return;
    this._request.abort();
    this.done = true;
    this.failed = true;
  }

  toString() {
    return `Ping started at ${new Date(this.start)}`;
  }

  _send() {
    this.start = Date.now();
    this._request = new Request();
    this._request.loaded.then(() => this._onPong(), () => this.abort());
  }

  _onPong() {
    this.done = true;
    this.end = Date.now();
    this.events.emit('pong');
  }
};
<commit_msg>Use HTTP/2 for pings<commit_after>const Events = require('events');

// Here we have a copy of the site in a CDN close to the user.
//
// Note we use HTTPS. On Cloudfront this causes the connection to be upgraded to
// HTTP/2 which causes the requests to reuse the same connection (thus avoiding
// the connection handshake at every ping). Also, HTTP caused problems on networks
// with captive portals (e.g. in many Internet cafes).
//
// Ensure the CDN is configured to not forward query strings to reduce latency.
const URL_ = 'https://d18ks85av1x0pi.cloudfront.net/scripts/blank.js?nocache';

class Script {
  constructor(url) {
    this._element = document.createElement('script');
    this._element.src = url;
    this.loaded = new Promise((resolve, reject) => {
      this._element.onload = resolve;
      this._element.onerror = reject;
    });
    document.head.appendChild(this._element);
  }

  remove() {
    if (!this._element) return;
    this._element.parentNode.removeChild(this._element);
    this._element = null;
  }
}

class Request {
  constructor() {
    this._script = new Script(`${URL_}&v=${Date.now()}`);
    this._script.loaded.then(() => this._script.remove(), () => this._script.remove());
  }

  abort() {
    this._script.remove();
  }

  get loaded() {
    return this._script.loaded;
  }
}

module.exports = class Ping {
  constructor() {
    this.events = new Events();
    this.done = false; // Whether it has finished (whether succesfully or not)
    this.failed = false; // Whether it finished failing
    this._send();
  }

  get lag() {
    return (this.end || Date.now()) - this.start;
  }

  /**
   * Cancels the ping
   */
  abort() {
    if (this.done) return;
    this._request.abort();
    this.done = true;
    this.failed = true;
  }

  toString() {
    return `Ping started at ${new Date(this.start)}`;
  }

  _send() {
    this.start = Date.now();
    this._request = new Request();
    this._request.loaded.then(() => this._onPong(), () => this.abort());
  }

  _onPong() {
    this.done = true;
    this.end = Date.now();
    this.events.emit('pong');
  }
};
<|endoftext|>