<commit_before>import { WebApp } from 'meteor/webapp';

// clever webAppConnectHandlersUse
export const webAppConnectHandlersUse = (name, route, fn, options) => {
  // init
  if (typeof name === 'function') {
    options = route;
    fn = name;
    route = '/';
    name = undefined;
  } else if (name[0] === '/') {
    options = fn;
    fn = route;
    route = name;
    name = undefined;
  } else if (typeof route === 'function') {
    options = fn;
    fn = route;
    route = '/';
  }
  options = options || {};
  route = options.route ? options.route : route;

  // newfn
  let done = false;
  const newfn = (req, res, next) => {
    if (!fn.stack && !fn._router && done && options.once) {
      next();
      return;
    }
    done = true;

    fn(req, res, next);

    if (!fn.stack && !fn._router && options.autoNext) {
      next();
    }
  };

  // use it
  let connectHandlers;
  if (options.raw) {
    connectHandlers = WebApp.rawConnectHandlers;
  } else {
    connectHandlers = WebApp.connectHandlers;
  }
  connectHandlers.use(route, newfn);

  // get handle
  let handle;
  if (options.unshift) {
    const item = connectHandlers.stack.pop();
    connectHandlers.stack.unshift(item);
    handle = connectHandlers.stack[0].handle;
  } else {
    handle = connectHandlers.stack[connectHandlers.stack.length - 1].handle;
  }

  // copy options to handle
  Object.keys(options).forEach((key) => {
    handle[key] = options[key];
  });
};

webAppConnectHandlersUse(function sortConnectHandlersMiddleware(req, res, next) {
  WebApp.rawConnectHandlers.stack.forEach((item) => {
    if (isNaN(item.handle.order)) {
      item.handle.order = 100;
    }
  });
  WebApp.connectHandlers.stack.forEach((item) => {
    if (isNaN(item.handle.order)) {
      item.handle.order = 100;
    }
  });
  WebApp.rawConnectHandlers.stack.sort((a, b) => a.handle.order - b.handle.order);
  WebApp.connectHandlers.stack.sort((a, b) => a.handle.order - b.handle.order);
}, { order: 0, autoNext: true, once: true, unshift: true });
<commit_msg>Comment out sortConnectHandlersMiddleware (breaks /graphql endpoint in some circumstances)<commit_after>import { WebApp } from 'meteor/webapp';

// NOTE: simplified version of webAppConnectHandlersUse that doesn't do anything
// export const webAppConnectHandlersUse = (app, options) => {
//   WebApp.connectHandlers.use(app);
// };

// TODO: figure out if still needed?
// clever webAppConnectHandlersUse
export const webAppConnectHandlersUse = (name, route, fn, options) => {
  // init
  if (typeof name === 'function') {
    options = route;
    fn = name;
    route = '/';
    name = undefined;
  } else if (name[0] === '/') {
    options = fn;
    fn = route;
    route = name;
    name = undefined;
  } else if (typeof route === 'function') {
    options = fn;
    fn = route;
    route = '/';
  }
  options = options || {};
  route = options.route ? options.route : route;

  // newfn
  let done = false;
  const newfn = (req, res, next) => {
    if (!fn.stack && !fn._router && done && options.once) {
      next();
      return;
    }
    done = true;

    fn(req, res, next);

    if (!fn.stack && !fn._router && options.autoNext) {
      next();
    }
  };

  // use it
  let connectHandlers;
  if (options.raw) {
    connectHandlers = WebApp.rawConnectHandlers;
  } else {
    connectHandlers = WebApp.connectHandlers;
  }
  connectHandlers.use(route, newfn);

  // get handle
  let handle;
  if (options.unshift) {
    const item = connectHandlers.stack.pop();
    connectHandlers.stack.unshift(item);
    handle = connectHandlers.stack[0].handle;
  } else {
    handle = connectHandlers.stack[connectHandlers.stack.length - 1].handle;
  }

  // copy options to handle
  Object.keys(options).forEach((key) => {
    handle[key] = options[key];
  });
};

// NOTE: breaks /graphql endpoint in production
// TODO: figure out what this does and if it's still needed?
// webAppConnectHandlersUse(function sortConnectHandlersMiddleware(req, res, next) {
//   WebApp.rawConnectHandlers.stack.forEach((item) => {
//     if (isNaN(item.handle.order)) {
//       item.handle.order = 100;
//     }
//   });
//   WebApp.connectHandlers.stack.forEach((item) => {
//     if (isNaN(item.handle.order)) {
//       item.handle.order = 100;
//     }
//   });
//   WebApp.rawConnectHandlers.stack.sort((a, b) => a.handle.order - b.handle.order);
//   WebApp.connectHandlers.stack.sort((a, b) => a.handle.order - b.handle.order);
// }, { order: 0, autoNext: true, once: true, unshift: true });
<|endoftext|>


<commit_before>var React = require('react');
var GoogleChartLoader = require('./GoogleChartLoader');
var DEFAULT_COLORS = require('../constants/DEFAULT_CHART_COLORS');

var uniqueId = 0;
var generateUniqueId = function () {
	uniqueId++;
	return "reactgooglegraph" + uniqueId;
};

var Chart = React.createClass({
	chart: null,
	wrapper: null,
	hidden_columns: [],
	data_table: [],
	getInitialState: function () {
		return {
			graph_id: this.props.graph_id || generateUniqueId()
		};
	},
	componentDidMount: function () {
		var self = this;

		GoogleChartLoader.init(this.props.chartPackages, this.props.chartVersion).then(function () {
			self.drawChart();
		});
	},

	componentDidUpdate: function () {
		if (GoogleChartLoader.is_loaded) {
			this.drawChart();
		}
		;
	},

	getDefaultProps: function () {
		return {
			chartType: 'LineChart',
			rows: [],
			columns: [],
			options: {
				chart: {
					title: 'Chart Title',
					subtitle: 'Subtitle'
				},
				hAxis: {title: 'X Label'},
				vAxis: {title: 'Y Label'},
				width: '400px',
				height: '300px'

			},
			chartEvents: [],
			chartActions: null,
			data: null,
			onSelect: null,
			legend_toggle: false
		};
	},

	render: function () {
		return React.DOM.div({id: this.state.graph_id, style: {height: this.props.height, width: this.props.width}});
	},
	build_data_table: function () {

		var data_table = new google.visualization.DataTable();
		for (var i = 0; i < this.props.columns.length; i++) {
			data_table.addColumn(this.props.columns[i]);
		}

		if (this.props.rows.length > 0) {
			data_table.addRows(this.props.rows);
		}
		return data_table;
	},

	update_data_table: function () {
		this.data_table.removeRows(0, this.data_table.getNumberOfRows());
		this.data_table.removeColumns(0, this.data_table.getNumberOfColumns());

		for (var i = 0; i < this.props.columns.length; i++) {
			this.data_table.addColumn(this.props.columns[i]);
		}

		if (this.props.rows.length > 0) {
			this.data_table.addRows(this.props.rows);
		}
	},

	drawChart: function () {

		if ((this.props.data !== null && this.props.data.length === 0) || this.props.columns.length === 0) {
			return;
		}

		if (!this.wrapper) {
			this.data_table = this.props.data !== null ? this.props.data : this.build_data_table();
			this.wrapper = new google.visualization.ChartWrapper({
				chartType: this.props.chartType,
				dataTable: this.data_table,
				options: this.props.options,
				containerId: this.state.graph_id
			});

			this.data_table = this.wrapper.getDataTable();

			var self = this;

			google.visualization.events.addOneTimeListener(this.wrapper, 'ready', function () {
				self.chart = self.wrapper.getChart();
				self.listen_to_chart_events.call(this);
				self.add_chart_actions.call(this);
			});

			if (this.props.legend_toggle) {
				google.visualization.events.addListener(this.wrapper, 'select', function () {
					self.default_chart_select.call(this);
				});
			}
			if (this.props.onSelect !== null) {
				google.visualization.events.addListener(this.wrapper, 'select', function () {
					self.props.onSelect(self, self.chart.getSelection());
				});
			}
		} else {
			if (this.props.data !== null) {
				this.wrapper.setDataTable(this.props.data);
				this.data_table = this.wrapper.getDataTable();
			} else {
				this.update_data_table();
			}
		}
		this.wrapper.draw();
	},

	listen_to_chart_events: function () {

		var self = this;
		var event_data;
		for (var i = 0; i < this.props.chartEvents.length; i++) {
			if (this.props.chartEvents[i].eventName === 'ready') {
				this.props.chartEvents[i].callback(this);
			}
			else {
				(function (callback) {
					google
							.visualization
							.events
							.addListener(self.chart, self.props.chartEvents[i].eventName, function (e) {
								callback(self, e);
							});
				})(self.props.chartEvents[i].callback)
			}
		}
	},
	add_chart_actions: function () {
		var self = this;
		// if any action was specified, add it to the chart
		if (this.props.chartActions != null) {
			self.chart.setAction({
				id: this.props.chartActions.id,
				text: this.props.chartActions.text,
				// bind the chart back to the action callback so we can get the chart information
				action: this.props.chartActions.action.bind(self.chart)
			});
		}
		;
	},
	default_chart_select: function () {
		var selection = this.chart.getSelection();
		// if selection length is 0, we deselected an element
		if (selection.length > 0) {
			// if row is undefined, we clicked on the legend
			if (selection[0].row == null) {
				var column = selection[0].column;
				this.toggle_points(column);
			}
		}
	},

	build_empty_column: function (index) {

		return {
			label: this.data_table.getColumnLabel(index),
			type: this.data_table.getColumnType(index),
			calc: function () {
				return null;
			}
		};
	},

	build_column_from_src: function (index) {
		return {
			label: this.data_table.getColumnLabel(index),
			type: this.data_table.getColumnType(index),
			sourceColumn: index
		};
	},

	toggle_points: function (column) {

		//Need to show legend !!

		var view = new google.visualization.DataView(this.wrapper.getDataTable());
		var column_count = view.getNumberOfColumns();
		var colors = [],
				columns = [],
				empty_columns = [],
				column_hidden,
				empty_column,
				column_from_src;

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

			// If user clicked on legend
			if (i === column) {

				column_hidden = (typeof this.hidden_columns[i] !== 'undefined');

				//User wants to hide values
				if (!column_hidden) {
					// Null out the values of the column
					empty_column = this.build_empty_column(i);
					columns.push(empty_column);

					this.hidden_columns[i] = {color: this.get_column_color(i - 1)};
					colors.push('#CCCCCC');
				}
				//User wants to show values
				else {
					column_from_src = this.build_column_from_src(i);
					columns.push(column_from_src);

					var previous_color = this.hidden_columns[i].color;
					delete this.hidden_columns[i];
					colors.push(previous_color);
				}
			}
			else if (typeof this.hidden_columns[i] !== 'undefined') {
				empty_column = this.build_empty_column(i);
				columns.push(empty_column);
				colors.push('#CCCCCC');
			}
			else {
				column_from_src = this.build_column_from_src(i);
				columns.push(column_from_src);

				if (i !== 0) {
					colors.push(this.get_column_color(i - 1));
				}
			}
		}

		view.setColumns(columns);
		this.props.options.colors = colors;

		this.chart.draw(view, this.props.options);
	},

	get_column_color: function (column_index) {
		if (this.props.options.colors) {
			if (this.props.options.colors[column_index]) {
				return this.props.options.colors[column_index];
			}
		}
		else {
			if (typeof DEFAULT_COLORS[column_index] !== 'undefined') {
				return DEFAULT_COLORS[column_index];
			}
			else {
				return DEFAULT_COLORS[0];
			}

		}
	}

});

module.exports = Chart;
<commit_msg>restored js style<commit_after>var React = require('react');
var GoogleChartLoader = require('./GoogleChartLoader');
var DEFAULT_COLORS = require('../constants/DEFAULT_CHART_COLORS');

var uniqueId = 0;
var generateUniqueId = function() {
	uniqueId++;
	return "reactgooglegraph" + uniqueId;
};

var Chart = React.createClass({
	chart: null,
	wrapper: null,
	hidden_columns: [],
	data_table: [],
	getInitialState: function() {
		return {
			graph_id: this.props.graph_id || generateUniqueId()
		};
	},
	componentDidMount: function(){
		var self = this;

		GoogleChartLoader.init(this.props.chartPackages, this.props.chartVersion).then(function(){
			self.drawChart();
		});
	},

	componentDidUpdate: function(){
		if (GoogleChartLoader.is_loaded){
			this.drawChart();
		};
	},

	getDefaultProps: function() {
		return {
			chartType : 'LineChart',
			rows: [],
			columns: [],
			options: {
				chart: {
					title: 'Chart Title',
					subtitle: 'Subtitle'
				},
				hAxis: {title: 'X Label'},
				vAxis: {title: 'Y Label'},
				width: '400px',
				height: '300px'

			},
			chartEvents : [],
			chartActions : null,
			data: null,
			onSelect: null,
			legend_toggle: false
		};
	},

	render: function() {
		return React.DOM.div({id: this.state.graph_id, style: {height: this.props.height, width:this.props.width}});
	},
	build_data_table : function() {

		var data_table = new google.visualization.DataTable();
		for (var i = 0 ; i < this.props.columns.length; i++) {
			data_table.addColumn(this.props.columns[i]);
		}

		if (this.props.rows.length > 0) {
			data_table.addRows(this.props.rows);
		}
		return data_table;
	},
	update_data_table : function() {
		this.data_table.removeRows(0, this.data_table.getNumberOfRows());
		this.data_table.removeColumns(0, this.data_table.getNumberOfColumns());

		for (var i = 0; i < this.props.columns.length; i++) {
			this.data_table.addColumn(this.props.columns[i]);
		}

		if (this.props.rows.length > 0) {
			this.data_table.addRows(this.props.rows);
		}
	},

	drawChart: function() {

		if ((this.props.data !== null && this.props.data.length === 0) || this.props.columns.length === 0) {
			return;
		}

		if (!this.wrapper) {
			this.data_table = this.props.data !== null ? this.props.data : this.build_data_table();
			this.wrapper = new google.visualization.ChartWrapper({
				chartType: this.props.chartType,
				dataTable: this.data_table,
				options: this.props.options,
				containerId: this.state.graph_id
			});

			this.data_table = this.wrapper.getDataTable();

			var self = this;

			google.visualization.events.addOneTimeListener(this.wrapper, 'ready', function () {
				self.chart = self.wrapper.getChart();
				self.listen_to_chart_events.call(this);
				self.add_chart_actions.call(this);
			});

			if (this.props.legend_toggle) {
				google.visualization.events.addListener(this.wrapper, 'select', function () {
					self.default_chart_select.call(this);
				});
			}
			if (this.props.onSelect !== null) {
				google.visualization.events.addListener(this.wrapper, 'select', function () {
					self.props.onSelect(self, self.chart.getSelection());
				});
			}
		} else {
			if (this.props.data !== null) {
				this.wrapper.setDataTable(this.props.data);
				this.data_table = this.wrapper.getDataTable();
			} else {
				this.update_data_table();
			}
		}
		this.wrapper.draw();
	},
	listen_to_chart_events: function() {

		var self = this;
		var event_data;
		for (var i = 0; i < this.props.chartEvents.length; i++) {
			if (this.props.chartEvents[i].eventName === 'ready') {
				this.props.chartEvents[i].callback(this);
			}
			else {
				(function(callback){
					google
							.visualization
							.events
							.addListener(self.chart, self.props.chartEvents[i].eventName, function (e){
								callback(self, e);
							});
				})(self.props.chartEvents[i].callback)
			}
		}
	},
	add_chart_actions: function () {
		var self = this;
		// if any action was specified, add it to the chart
		if (this.props.chartActions != null) {
			self.chart.setAction({
				id: this.props.chartActions.id,
				text: this.props.chartActions.text,
				// bind the chart back to the action callback so we can get the chart information
				action: this.props.chartActions.action.bind(self.chart)
			});
		};
	},
	default_chart_select: function() {
		var selection = this.chart.getSelection();
		// if selection length is 0, we deselected an element
		if (selection.length > 0) {
			// if row is undefined, we clicked on the legend
			if (selection[0].row == null) {
				var column = selection[0].column;
				this.toggle_points(column);
			}
		}
	},

	build_empty_column: function(index) {

		return {
			label: this.data_table.getColumnLabel(index),
			type: this.data_table.getColumnType(index),
			calc: function () {
				return null;
			}
		};
	},

	build_column_from_src: function(index) {
		return {
			label: this.data_table.getColumnLabel(index),
			type: this.data_table.getColumnType(index),
			sourceColumn: index
		};
	},

	toggle_points: function(column) {

		//Need to show legend !!

		var view = new google.visualization.DataView(this.wrapper.getDataTable());
		var column_count = view.getNumberOfColumns();
		var colors = [],
				columns = [],
				empty_columns = [],
				column_hidden,
				empty_column,
				column_from_src;

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

			// If user clicked on legend
			if (i === column ) {

				column_hidden = (typeof this.hidden_columns[i] !== 'undefined');

				//User wants to hide values
				if (!column_hidden ) {
					// Null out the values of the column
					empty_column =  this.build_empty_column(i);
					columns.push(empty_column);

					this.hidden_columns[i] = { color : this.get_column_color(i-1) };
					colors.push('#CCCCCC');
				}
				//User wants to show values
				else {
					column_from_src = this.build_column_from_src(i);
					columns.push(column_from_src);

					var previous_color = this.hidden_columns[i].color;
					delete this.hidden_columns[i];
					colors.push(previous_color);
				}
			}
			else if (typeof this.hidden_columns[i] !== 'undefined') {
				empty_column =  this.build_empty_column(i);
				columns.push(empty_column);
				colors.push('#CCCCCC');
			}
			else {
				column_from_src = this.build_column_from_src(i);
				columns.push(column_from_src);

				if (i !== 0) {
					colors.push(this.get_column_color(i-1));
				}
			}
		}

		view.setColumns(columns);
		this.props.options.colors = colors;

		this.chart.draw(view, this.props.options);
	},

	get_column_color: function(column_index) {
		if (this.props.options.colors) {
			if (this.props.options.colors[column_index]) {
				return this.props.options.colors[column_index];
			}
		}
		else {
			if (typeof DEFAULT_COLORS[column_index] !== 'undefined') {
				return DEFAULT_COLORS[column_index];
			}
			else {
				return DEFAULT_COLORS[0];
			}

		}
	}

});

module.exports = Chart;<|endoftext|>


<commit_before>import { addCallback, Utils } from 'meteor/vulcan:core';
import Users from 'meteor/vulcan:users';
import { getVotePower } from '../modules/vote.js';
import { updateScore } from './scoring.js';

// ----------------------------- vote.async ------------------------------- //

/**
 * @summary Update an item's (post or comment) score
 * @param {object} item - The item being operated on
 * @param {object} user - The user doing the operation
 * @param {object} collection - The collection the item belongs to
 * @param {string} operation - The operation being performed
 */
// function updateItemScore(item, user, collection, operation, context) {
//   updateScore({collection: collection, item: item, forceUpdate: true});
// }

// addCallback("upvote.async", updateItemScore);
// addCallback("downvote.async", updateItemScore);
// addCallback("cancelUpvote.async", updateItemScore);
// addCallback("cancelDownvote.async", updateItemScore);

/**
 * @summary Update the profile of the user doing the operation
 * @param {object} item - The item being operated on
 * @param {object} user - The user doing the operation
 * @param {object} collection - The collection the item belongs to
 * @param {string} operation - The operation being performed
 */
// function updateUser(item, user, collection, operation, context) {

//   // uncomment for debug
//   // console.log(item);
//   // console.log(user);
//   // console.log(collection._name);
//   // console.log(operation);

//   const update = {};
//   const votePower = getVotePower(user);
//   const vote = {
//     itemId: item._id,
//     votedAt: new Date(),
//     power: votePower
//   };

//   const collectionName = Utils.capitalize(collection._name);

//   switch (operation) {
//     case "upvote":
//       update.$addToSet = {[`upvoted${collectionName}`]: vote};
//       break;
//     case "downvote":
//       update.$addToSet = {[`downvoted${collectionName}`]: vote};
//       break;
//     case "cancelUpvote":
//       update.$pull = {[`upvoted${collectionName}`]: {itemId: item._id}};
//       break;
//     case "cancelDownvote":
//       update.$pull = {[`downvoted${collectionName}`]: {itemId: item._id}};
//       break;
//   }

//   Users.update({_id: user._id}, update);

// }

// addCallback("upvote.async", updateUser);
// addCallback("downvote.async", updateUser);
// addCallback("cancelUpvote.async", updateUser);
// addCallback("cancelDownvote.async", updateUser);

/**
 * @summary Update the karma of the item's owner
 * @param {object} item - The item being operated on
 * @param {object} user - The user doing the operation
 * @param {object} collection - The collection the item belongs to
 * @param {string} operation - The operation being performed
 */
function updateKarma({newDocument, vote}, collection, user, context) {
  let karmaAmount = 0;
  if (vote.voteType === 'upvote') {
    karmaAmount = vote.power;
  } else if (vote.voteType === 'downvote') {
    karmaAmount = -vote.power;
  }
  console.log('updateKarma', newDocument, vote);
  // only update karma is the operation isn't done by the item's author
  if (newDocument.userId !== vote.userId) {
    console.log("Increasing user x karma by y", newDocument.userId, karmaAmount)
    Users.update({_id: newDocument.userId}, {$inc: {"karma": karmaAmount}});
  }
}

addCallback("votes.upvote.async", updateKarma);
addCallback("votes.downvote.async", updateKarma);

function cancelVoteKarma({newDocument, vote}, collection, user, context) {
  let karmaAmount = 0;
  if (vote.voteType === 'upvote') {
    karmaAmount = -vote.power;
  } else if (vote.voteType === 'downvote') {
    karmaAmount = vote.power;
  }
  console.log('cancelVoteKarma', newDocument, vote);
  // only update karma is the operation isn't done by the item's author
  if (newDocument.userId !== vote.userId) {
    console.log("Increasing user x karma by y", newDocument.userId, karmaAmount)
    Users.update({_id: newDocument.userId}, {$inc: {"karma": karmaAmount}});
  }
}

addCallback("votes.cancel.async", cancelVoteKarma);
<commit_msg>Fixed vote karma callback<commit_after>import { addCallback, Utils } from 'meteor/vulcan:core';
import Users from 'meteor/vulcan:users';
import { getVotePower } from '../modules/vote.js';
import { updateScore } from './scoring.js';

// ----------------------------- vote.async ------------------------------- //

/**
 * @summary Update an item's (post or comment) score
 * @param {object} item - The item being operated on
 * @param {object} user - The user doing the operation
 * @param {object} collection - The collection the item belongs to
 * @param {string} operation - The operation being performed
 */
// function updateItemScore(item, user, collection, operation, context) {
//   updateScore({collection: collection, item: item, forceUpdate: true});
// }

// addCallback("upvote.async", updateItemScore);
// addCallback("downvote.async", updateItemScore);
// addCallback("cancelUpvote.async", updateItemScore);
// addCallback("cancelDownvote.async", updateItemScore);

/**
 * @summary Update the profile of the user doing the operation
 * @param {object} item - The item being operated on
 * @param {object} user - The user doing the operation
 * @param {object} collection - The collection the item belongs to
 * @param {string} operation - The operation being performed
 */
// function updateUser(item, user, collection, operation, context) {

//   // uncomment for debug
//   // console.log(item);
//   // console.log(user);
//   // console.log(collection._name);
//   // console.log(operation);

//   const update = {};
//   const votePower = getVotePower(user);
//   const vote = {
//     itemId: item._id,
//     votedAt: new Date(),
//     power: votePower
//   };

//   const collectionName = Utils.capitalize(collection._name);

//   switch (operation) {
//     case "upvote":
//       update.$addToSet = {[`upvoted${collectionName}`]: vote};
//       break;
//     case "downvote":
//       update.$addToSet = {[`downvoted${collectionName}`]: vote};
//       break;
//     case "cancelUpvote":
//       update.$pull = {[`upvoted${collectionName}`]: {itemId: item._id}};
//       break;
//     case "cancelDownvote":
//       update.$pull = {[`downvoted${collectionName}`]: {itemId: item._id}};
//       break;
//   }

//   Users.update({_id: user._id}, update);

// }

// addCallback("upvote.async", updateUser);
// addCallback("downvote.async", updateUser);
// addCallback("cancelUpvote.async", updateUser);
// addCallback("cancelDownvote.async", updateUser);

/**
 * @summary Update the karma of the item's owner
 * @param {object} item - The item being operated on
 * @param {object} user - The user doing the operation
 * @param {object} collection - The collection the item belongs to
 * @param {string} operation - The operation being performed
 */
function updateKarma({newDocument, vote}, collection, user, context) {
  // only update karma is the operation isn't done by the item's author
  if (newDocument.userId !== vote.userId) {
    console.log("Increasing user x karma by y", newDocument.userId, vote.power)
    Users.update({_id: newDocument.userId}, {$inc: {"karma": vote.power}});
  }
}

addCallback("votes.upvote.async", updateKarma);
addCallback("votes.downvote.async", updateKarma);

function cancelVoteKarma({newDocument, vote}, collection, user, context) {
  // only update karma is the operation isn't done by the item's author
  if (newDocument.userId !== vote.userId) {
    console.log("Increasing user x karma by y", newDocument.userId, -vote.power)
    Users.update({_id: newDocument.userId}, {$inc: {"karma": -vote.power}});
  }
}

addCallback("votes.cancel.async", cancelVoteKarma);
<|endoftext|>


<commit_before>/* eslint react/forbid-prop-types: "off" */
import React from 'react';
import Debug from 'debug';

import DEFAULT_COLORS from '../constants/DEFAULT_CHART_COLORS';
import googleChartLoader from './GoogleChartLoader';

const debug = new Debug('react-google-charts:Chart');

let uniqueID = 0;

const generateUniqueID = () => {
  uniqueID += 1;
  return `reactgooglegraph-${uniqueID}`;
};

export default class Chart extends React.Component {

  constructor(props) {
    debug('constructor', props);
    super(props);
    this.state = { graphID: props.graph_id || generateUniqueID() };
    this.chart = null;
    this.wrapper = null;
    this.hidden_columns = {};
    this.dataTable = [];

    this.debounce = this.debounce.bind(this);
    this.onResize = this.onResize.bind(this);
    this.drawChart = this.drawChart.bind(this);
    this.togglePoints = this.togglePoints.bind(this);
    this.buildDataTableFromProps = this.buildDataTableFromProps.bind(this);
    this.listenToChartEvents = this.listenToChartEvents.bind(this);
    this.addChartActions = this.addChartActions.bind(this);
    this.updateDataTable = this.updateDataTable.bind(this);
    this.onSelectToggle = this.onSelectToggle.bind(this);
    this.addSourceColumnTo = this.addSourceColumnTo.bind(this);
    this.restoreColorTo = this.restoreColorTo.bind(this);
    this.hideColumn = this.hideColumn.bind(this);
  }
  componentDidMount() {
    debug('componentDidMount');
    if (typeof window === 'undefined') {
      return;
    }
    if (this.props.loadCharts) {
      googleChartLoader.init(this.props.chartPackages, this.props.chartVersion).then(() => {
        this.drawChart();
      });
      window.addEventListener('resize', this.onResize);
      this.onResize = this.debounce(this.onResize, 200);
    } else {
      this.drawChart();
    }
  }

  componentDidUpdate() {
    debug('componentDidUpdate');
    if (!this.props.loadCharts) {
      this.drawChart();
    } else if (googleChartLoader.isLoading) {
      googleChartLoader.initPromise.then(() => {
        this.drawChart();
      });
    } else if (googleChartLoader.isLoaded) {
      this.drawChart();
    }
  }
  componentWillUnmount() {
    try {
      if(window) {
        if (window.google && window.google.visualization) {
          window.google.visualization.events.removeAllListeners(this.wrapper);
        }
        window.removeEventListener('resize', this.onResize);
      }
    } catch (err) {
      console.error('Error removing events, error : ', err);
    }
  }

  debounce(func, wait) {
    let timeout;
    return function(...args) {
      const context = this;
      clearTimeout(timeout);
      timeout = setTimeout(() => func.apply(context, args), wait);
    };
  }

  onResize() {
    console.log('Chart::onResize');
    this.drawChart();
  }

  onSelectToggle() {
    debug('onSelectToggle');
    const selection = this.chart.getSelection();
    if (selection.length > 0) {
      if (selection[0].row == null) {
        const column = selection[0].column;
        this.togglePoints(column);
      }
    }
  }

  getColumnColor(columnIndex) {
    if (this.props.options.colors) {
      if (this.props.options.colors[columnIndex]) {
        return this.props.options.colors[columnIndex];
      }
    } else if (columnIndex in DEFAULT_COLORS) {
      return DEFAULT_COLORS[columnIndex];
    }
    return DEFAULT_COLORS[0];
  }

  buildDataTableFromProps() {
    debug('buildDataTableFromProps', this.props);

    if (this.props.diffdata) {
      const diffdata = this.props.diffdata;
      const oldData = window.google.visualization.arrayToDataTable(diffdata.old);
      const newData = window.google.visualization.arrayToDataTable(diffdata.new);
        // must take computeDiff from prototypes since not available with charts early in process
      const computeDiff = window.google.visualization[this.props.chartType].prototype.computeDiff;
      const chartDiff = computeDiff(oldData, newData);
      return chartDiff;
    }

    if (this.props.data === null && this.props.rows.length === 0) {
      throw new Error("Can't build DataTable from rows and columns: rows array in props is empty");
    } else if (this.props.data === null && this.props.columns.length === 0) {
      throw new Error("Can't build DataTable from rows and columns: columns array in props is empty");
    }
    if (this.props.data !== null) {
      try {
        this.wrapper.setDataTable(this.props.data);
        const dataTable = this.wrapper.getDataTable();
        return dataTable;
      } catch (err) {
        console.error('Failed to set DataTable from data props ! ', err);
        throw new Error('Failed to set DataTable from data props ! ', err);
      }
    }

    const dataTable = new window.google.visualization.DataTable();
    this.props.columns.forEach((column) => {
      dataTable.addColumn(column);
    });
    dataTable.addRows(this.props.rows);
    if (this.props.numberFormat) {
      const formatter = new window.google.visualization.NumberFormat(
        this.props.numberFormat.options
      );
      formatter.format(dataTable, this.props.numberFormat.column);
    }
    return dataTable;
  }
  updateDataTable() {
    debug('updateDataTable');
    window.google.visualization.errors.removeAll(
      document.getElementById(this.wrapper.getContainerId())
    );
    this.dataTable.removeRows(0, this.dataTable.getNumberOfRows());
    this.dataTable.removeColumns(0, this.dataTable.getNumberOfColumns());
    this.dataTable = this.buildDataTableFromProps();
    return this.dataTable;
  }

  drawChart() {
    debug('drawChart', this);
    if (!this.wrapper) {
      const chartConfig = {
        chartType: this.props.chartType,
        options: this.props.options,
        containerId: this.state.graphID,
      };
      this.wrapper = new window.google.visualization.ChartWrapper(chartConfig);
      this.dataTable = this.buildDataTableFromProps();
      this.wrapper.setDataTable(this.dataTable);

      window.google.visualization.events.addOneTimeListener(this.wrapper, 'ready', () => {
        this.chart = this.wrapper.getChart();
        this.listenToChartEvents();
        this.addChartActions();
      });
    } else {
      this.updateDataTable();
      this.wrapper.setDataTable(this.dataTable);
       // this.wrapper.setChartType(this.props.chartType)
      this.wrapper.setOptions(this.props.options);
      if (this.wrapper.getChartType() !== this.props.chartType) {
        window.google.visualization.events.removeAllListeners(this.wrapper);
        this.wrapper.setChartType(this.props.chartType);
        const self = this;
        window.google.visualization.events.addOneTimeListener(this.wrapper, 'ready', () => {
          self.chart = self.wrapper.getChart();
          self.listenToChartEvents.call(self);
        });
      }
    }
    this.wrapper.draw();
  }

  addChartActions() {
    debug('addChartActions', this.props.chartActions);
    if (this.props.chartActions === null) {
      return;
    }
    this.props.chartActions.forEach((chartAction) => {
      this.chart.setAction({
        id: chartAction.id,
        text: chartAction.text,
        action: chartAction.action.bind(this, this.chart),
      });
    });
  }

  listenToChartEvents() {
    debug('listenToChartEvents', this.props.legend_toggle, this.props.chartEvents);
    if (this.props.legend_toggle) {
      window.google.visualization.events.addListener(
        this.wrapper,
        'select',
        this.onSelectToggle
      );
    }
    this.props.chartEvents.forEach((chartEvent) => {
      if (chartEvent.eventName === 'ready') {
        chartEvent.callback(this);
      } else {
        ((event) => {
          window.google.visualization.events.addListener(this.chart, event.eventName, (e) => {
            event.callback(this, e);
          });
        })(chartEvent);
      }
    });
  }

  buildColumnFromSourceData(columnIndex) {
    debug('buildColumnFromSourceData', columnIndex);
    return {
      label: this.dataTable.getColumnLabel(columnIndex),
      type: this.dataTable.getColumnType(columnIndex),
      sourceColumn: columnIndex,
    };
  }

  buildEmptyColumnFromSourceData(columnIndex) {
    debug('buildEmptyColumnFromSourceData', columnIndex);
    return {
      label: this.dataTable.getColumnLabel(columnIndex),
      type: this.dataTable.getColumnType(columnIndex),
      calc: () => null,
    };
  }
  addEmptyColumnTo(columns, columnIndex) {
    debug('addEmptyColumnTo', columns, columnIndex);
    const emptyColumn = this.buildEmptyColumnFromSourceData(columnIndex);
    columns.push(emptyColumn);
  }

  hideColumn(colors, columnIndex) {
    debug('hideColumn', colors, columnIndex);
    if (!this.isHidden(columnIndex)) {
      this.hidden_columns[columnIndex] = { color: this.getColumnColor(columnIndex - 1) };
    }
    colors.push('#CCCCCC');
  }
  addSourceColumnTo(columns, columnIndex) {
    debug('addSourceColumnTo', columns, columnIndex);
    const sourceColumn = this.buildColumnFromSourceData(columnIndex);
    columns.push(sourceColumn);
  }

  isHidden(columnIndex) {
    return this.hidden_columns[columnIndex] !== undefined;
  }
  restoreColorTo(colors, columnIndex) {
    debug('restoreColorTo', colors, columnIndex);
    debug('hidden_columns', this.hidden_columns);
    let previousColor;
    if (this.isHidden(columnIndex)) {
      previousColor = this.hidden_columns[columnIndex].color;
      delete this.hidden_columns[columnIndex];
    } else {
      previousColor = this.getColumnColor(columnIndex - 1);
    }
    if (columnIndex !== 0) {
      colors.push(previousColor);
    }
  }

  togglePoints(column) {
    debug('togglePoints', column);
    const view = new window.google.visualization.DataView(this.wrapper.getDataTable());
    const columnCount = view.getNumberOfColumns();
    let colors = []; // eslint-disable-line prefer-const
    let columns = []; // eslint-disable-line prefer-const
    for (let i = 0; i < columnCount; i += 1) {
      // If user clicked on legend
      if (i === 0) {
        this.addSourceColumnTo(columns, i);
      } else if (i === column) {
        if (this.isHidden(i)) {
          this.addSourceColumnTo(columns, i);
          this.restoreColorTo(colors, i);
        } else {
          this.addEmptyColumnTo(columns, i);
          this.hideColumn(colors, i);
        }
      } else if (this.isHidden(i)) {
        this.addEmptyColumnTo(columns, i);
        this.hideColumn(colors, i);
      } else {
        this.addSourceColumnTo(columns, i);
        this.restoreColorTo(colors, i);
      }
    }
    view.setColumns(columns);
    this.props.options.colors = colors;
    this.chart.draw(view, this.props.options);
  }

  render() {
    debug('render', this.props, this.state);
    const divStyle = {
      height: this.props.height || this.props.options.height,
      width: this.props.width || this.props.options.width,
    };
    return (

        {this.props.loader ? this.props.loader : 'Rendering Chart...'}

    );
  }
}

Chart.propTypes = {
  graph_id: React.PropTypes.string,
  chartType: React.PropTypes.string,
  rows: React.PropTypes.arrayOf(React.PropTypes.array),
  columns: React.PropTypes.arrayOf(React.PropTypes.object),
  data: React.PropTypes.arrayOf(React.PropTypes.array),
  options: React.PropTypes.any,
  width: React.PropTypes.string,
  height: React.PropTypes.string,
  chartEvents: React.PropTypes.arrayOf(React.PropTypes.shape({
    // https://github.com/yannickcr/eslint-plugin-react/issues/819
    eventName: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    callback: React.PropTypes.func, // eslint-disable-line react/no-unused-prop-types
  })),
  chartActions: React.PropTypes.arrayOf(React.PropTypes.shape({
    id: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    text: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    action: React.PropTypes.func, // eslint-disable-line react/no-unused-prop-types
  })),
  loadCharts: React.PropTypes.bool,
  loader: React.PropTypes.node,
  legend_toggle: React.PropTypes.bool,
  chartPackages: React.PropTypes.arrayOf(React.PropTypes.string),
  chartVersion: React.PropTypes.string,
  numberFormat: React.PropTypes.shape({
    column: React.PropTypes.number, // eslint-disable-line react/no-unused-prop-types
    options: React.PropTypes.shape({
      decimalSymbol: React.PropTypes.string,  // eslint-disable-line react/no-unused-prop-types
      fractionDigits: React.PropTypes.number, // eslint-disable-line react/no-unused-prop-types
      groupingSymbol: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      negativeColor: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      negativeParens: React.PropTypes.bool, // eslint-disable-line react/no-unused-prop-types
      pattern: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      prefix: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      suffix: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    }),
  }),
  diffdata: React.PropTypes.shape({
    on: React.PropTypes.array, // eslint-disable-line react/no-unused-prop-types
    off: React.PropTypes.array, // eslint-disable-line react/no-unused-prop-types
  }),
};

Chart.defaultProps = {
  chartType: 'LineChart',
  rows: [],
  columns: [],
  options: {
    chart: {
      title: 'Chart Title',
      subtitle: 'Subtitle',
    },
    hAxis: { title: 'X Label' },
    vAxis: { title: 'Y Label' },
    width: '400px',
    height: '300px',
  },
  width: '400px',
  height: '300px',
  chartEvents: [],
  chartActions: null,
  data: null,
  legend_toggle: false,
  loadCharts: true,
  loader:
Rendering Chart
,
  chartPackages: ['corechart'],
  chartVersion: 'current',
  numberFormat: null,
  diffdata: null,
};
<commit_msg>Linting fixes<commit_after>/* eslint react/forbid-prop-types: "off" */
import React from 'react';
import Debug from 'debug';

import DEFAULT_COLORS from '../constants/DEFAULT_CHART_COLORS';
import googleChartLoader from './GoogleChartLoader';

const debug = new Debug('react-google-charts:Chart');

let uniqueID = 0;

const generateUniqueID = () => {
  uniqueID += 1;
  return `reactgooglegraph-${uniqueID}`;
};

export default class Chart extends React.Component {

  constructor(props) {
    debug('constructor', props);
    super(props);
    this.state = { graphID: props.graph_id || generateUniqueID() };
    this.chart = null;
    this.wrapper = null;
    this.hidden_columns = {};
    this.dataTable = [];

    this.debounce = this.debounce.bind(this);
    this.onResize = this.onResize.bind(this);
    this.drawChart = this.drawChart.bind(this);
    this.togglePoints = this.togglePoints.bind(this);
    this.buildDataTableFromProps = this.buildDataTableFromProps.bind(this);
    this.listenToChartEvents = this.listenToChartEvents.bind(this);
    this.addChartActions = this.addChartActions.bind(this);
    this.updateDataTable = this.updateDataTable.bind(this);
    this.onSelectToggle = this.onSelectToggle.bind(this);
    this.addSourceColumnTo = this.addSourceColumnTo.bind(this);
    this.restoreColorTo = this.restoreColorTo.bind(this);
    this.hideColumn = this.hideColumn.bind(this);
  }
  componentDidMount() {
    debug('componentDidMount');
    if (typeof window === 'undefined') {
      return;
    }
    if (this.props.loadCharts) {
      googleChartLoader.init(this.props.chartPackages, this.props.chartVersion).then(() => {
        this.drawChart();
      });
      window.addEventListener('resize', this.onResize);
      this.onResize = this.debounce(this.onResize, 200);
    } else {
      this.drawChart();
    }
  }

  componentDidUpdate() {
    debug('componentDidUpdate');
    if (!this.props.loadCharts) {
      this.drawChart();
    } else if (googleChartLoader.isLoading) {
      googleChartLoader.initPromise.then(() => {
        this.drawChart();
      });
    } else if (googleChartLoader.isLoaded) {
      this.drawChart();
    }
  }
  componentWillUnmount() {
    try {
      if (window) {
        if (window.google && window.google.visualization) {
          window.google.visualization.events.removeAllListeners(this.wrapper);
        }
        window.removeEventListener('resize', this.onResize);
      }
    } catch (err) {
      console.error('Error removing events, error : ', err);
    }
  }

  onResize() {
    console.log('Chart::onResize');
    this.drawChart();
  }

  onSelectToggle() {
    debug('onSelectToggle');
    const selection = this.chart.getSelection();
    if (selection.length > 0) {
      if (selection[0].row == null) {
        const column = selection[0].column;
        this.togglePoints(column);
      }
    }
  }

  getColumnColor(columnIndex) {
    if (this.props.options.colors) {
      if (this.props.options.colors[columnIndex]) {
        return this.props.options.colors[columnIndex];
      }
    } else if (columnIndex in DEFAULT_COLORS) {
      return DEFAULT_COLORS[columnIndex];
    }
    return DEFAULT_COLORS[0];
  }

  buildDataTableFromProps() {
    debug('buildDataTableFromProps', this.props);

    if (this.props.diffdata) {
      const diffdata = this.props.diffdata;
      const oldData = window.google.visualization.arrayToDataTable(diffdata.old);
      const newData = window.google.visualization.arrayToDataTable(diffdata.new);
        // must take computeDiff from prototypes since not available with charts early in process
      const computeDiff = window.google.visualization[this.props.chartType].prototype.computeDiff;
      const chartDiff = computeDiff(oldData, newData);
      return chartDiff;
    }

    if (this.props.data === null && this.props.rows.length === 0) {
      throw new Error("Can't build DataTable from rows and columns: rows array in props is empty");
    } else if (this.props.data === null && this.props.columns.length === 0) {
      throw new Error("Can't build DataTable from rows and columns: columns array in props is empty");
    }
    if (this.props.data !== null) {
      try {
        this.wrapper.setDataTable(this.props.data);
        const dataTable = this.wrapper.getDataTable();
        return dataTable;
      } catch (err) {
        console.error('Failed to set DataTable from data props ! ', err);
        throw new Error('Failed to set DataTable from data props ! ', err);
      }
    }

    const dataTable = new window.google.visualization.DataTable();
    this.props.columns.forEach((column) => {
      dataTable.addColumn(column);
    });
    dataTable.addRows(this.props.rows);
    if (this.props.numberFormat) {
      const formatter = new window.google.visualization.NumberFormat(
        this.props.numberFormat.options
      );
      formatter.format(dataTable, this.props.numberFormat.column);
    }
    return dataTable;
  }
  updateDataTable() {
    debug('updateDataTable');
    window.google.visualization.errors.removeAll(
      document.getElementById(this.wrapper.getContainerId())
    );
    this.dataTable.removeRows(0, this.dataTable.getNumberOfRows());
    this.dataTable.removeColumns(0, this.dataTable.getNumberOfColumns());
    this.dataTable = this.buildDataTableFromProps();
    return this.dataTable;
  }

  drawChart() {
    debug('drawChart', this);
    if (!this.wrapper) {
      const chartConfig = {
        chartType: this.props.chartType,
        options: this.props.options,
        containerId: this.state.graphID,
      };
      this.wrapper = new window.google.visualization.ChartWrapper(chartConfig);
      this.dataTable = this.buildDataTableFromProps();
      this.wrapper.setDataTable(this.dataTable);

      window.google.visualization.events.addOneTimeListener(this.wrapper, 'ready', () => {
        this.chart = this.wrapper.getChart();
        this.listenToChartEvents();
        this.addChartActions();
      });
    } else {
      this.updateDataTable();
      this.wrapper.setDataTable(this.dataTable);
       // this.wrapper.setChartType(this.props.chartType)
      this.wrapper.setOptions(this.props.options);
      if (this.wrapper.getChartType() !== this.props.chartType) {
        window.google.visualization.events.removeAllListeners(this.wrapper);
        this.wrapper.setChartType(this.props.chartType);
        const self = this;
        window.google.visualization.events.addOneTimeListener(this.wrapper, 'ready', () => {
          self.chart = self.wrapper.getChart();
          self.listenToChartEvents.call(self);
        });
      }
    }
    this.wrapper.draw();
  }

  addChartActions() {
    debug('addChartActions', this.props.chartActions);
    if (this.props.chartActions === null) {
      return;
    }
    this.props.chartActions.forEach((chartAction) => {
      this.chart.setAction({
        id: chartAction.id,
        text: chartAction.text,
        action: chartAction.action.bind(this, this.chart),
      });
    });
  }

  listenToChartEvents() {
    debug('listenToChartEvents', this.props.legend_toggle, this.props.chartEvents);
    if (this.props.legend_toggle) {
      window.google.visualization.events.addListener(
        this.wrapper,
        'select',
        this.onSelectToggle
      );
    }
    this.props.chartEvents.forEach((chartEvent) => {
      if (chartEvent.eventName === 'ready') {
        chartEvent.callback(this);
      } else {
        ((event) => {
          window.google.visualization.events.addListener(this.chart, event.eventName, (e) => {
            event.callback(this, e);
          });
        })(chartEvent);
      }
    });
  }

  buildColumnFromSourceData(columnIndex) {
    debug('buildColumnFromSourceData', columnIndex);
    return {
      label: this.dataTable.getColumnLabel(columnIndex),
      type: this.dataTable.getColumnType(columnIndex),
      sourceColumn: columnIndex,
    };
  }

  buildEmptyColumnFromSourceData(columnIndex) {
    debug('buildEmptyColumnFromSourceData', columnIndex);
    return {
      label: this.dataTable.getColumnLabel(columnIndex),
      type: this.dataTable.getColumnType(columnIndex),
      calc: () => null,
    };
  }
  addEmptyColumnTo(columns, columnIndex) {
    debug('addEmptyColumnTo', columns, columnIndex);
    const emptyColumn = this.buildEmptyColumnFromSourceData(columnIndex);
    columns.push(emptyColumn);
  }

  hideColumn(colors, columnIndex) {
    debug('hideColumn', colors, columnIndex);
    if (!this.isHidden(columnIndex)) {
      this.hidden_columns[columnIndex] = { color: this.getColumnColor(columnIndex - 1) };
    }
    colors.push('#CCCCCC');
  }
  addSourceColumnTo(columns, columnIndex) {
    debug('addSourceColumnTo', columns, columnIndex);
    const sourceColumn = this.buildColumnFromSourceData(columnIndex);
    columns.push(sourceColumn);
  }

  isHidden(columnIndex) {
    return this.hidden_columns[columnIndex] !== undefined;
  }
  restoreColorTo(colors, columnIndex) {
    debug('restoreColorTo', colors, columnIndex);
    debug('hidden_columns', this.hidden_columns);
    let previousColor;
    if (this.isHidden(columnIndex)) {
      previousColor = this.hidden_columns[columnIndex].color;
      delete this.hidden_columns[columnIndex];
    } else {
      previousColor = this.getColumnColor(columnIndex - 1);
    }
    if (columnIndex !== 0) {
      colors.push(previousColor);
    }
  }
  // eslint-disable-next-line class-methods-use-this
  debounce(func, wait) {
    let timeout;
    return function (...args) {
      const context = this;
      clearTimeout(timeout);
      timeout = setTimeout(() => func.apply(context, args), wait);
    };
  }
  togglePoints(column) {
    debug('togglePoints', column);
    const view = new window.google.visualization.DataView(this.wrapper.getDataTable());
    const columnCount = view.getNumberOfColumns();
    let colors = []; // eslint-disable-line prefer-const
    let columns = []; // eslint-disable-line prefer-const
    for (let i = 0; i < columnCount; i += 1) {
      // If user clicked on legend
      if (i === 0) {
        this.addSourceColumnTo(columns, i);
      } else if (i === column) {
        if (this.isHidden(i)) {
          this.addSourceColumnTo(columns, i);
          this.restoreColorTo(colors, i);
        } else {
          this.addEmptyColumnTo(columns, i);
          this.hideColumn(colors, i);
        }
      } else if (this.isHidden(i)) {
        this.addEmptyColumnTo(columns, i);
        this.hideColumn(colors, i);
      } else {
        this.addSourceColumnTo(columns, i);
        this.restoreColorTo(colors, i);
      }
    }
    view.setColumns(columns);
    this.props.options.colors = colors;
    this.chart.draw(view, this.props.options);
  }

  render() {
    debug('render', this.props, this.state);
    const divStyle = {
      height: this.props.height || this.props.options.height,
      width: this.props.width || this.props.options.width,
    };
    return (

        {this.props.loader ? this.props.loader : 'Rendering Chart...'}

    );
  }
}

Chart.propTypes = {
  graph_id: React.PropTypes.string,
  chartType: React.PropTypes.string,
  rows: React.PropTypes.arrayOf(React.PropTypes.array),
  columns: React.PropTypes.arrayOf(React.PropTypes.object),
  data: React.PropTypes.arrayOf(React.PropTypes.array),
  options: React.PropTypes.any,
  width: React.PropTypes.string,
  height: React.PropTypes.string,
  chartEvents: React.PropTypes.arrayOf(React.PropTypes.shape({
    // https://github.com/yannickcr/eslint-plugin-react/issues/819
    eventName: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    callback: React.PropTypes.func, // eslint-disable-line react/no-unused-prop-types
  })),
  chartActions: React.PropTypes.arrayOf(React.PropTypes.shape({
    id: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    text: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    action: React.PropTypes.func, // eslint-disable-line react/no-unused-prop-types
  })),
  loadCharts: React.PropTypes.bool,
  loader: React.PropTypes.node,
  legend_toggle: React.PropTypes.bool,
  chartPackages: React.PropTypes.arrayOf(React.PropTypes.string),
  chartVersion: React.PropTypes.string,
  numberFormat: React.PropTypes.shape({
    column: React.PropTypes.number, // eslint-disable-line react/no-unused-prop-types
    options: React.PropTypes.shape({
      decimalSymbol: React.PropTypes.string,  // eslint-disable-line react/no-unused-prop-types
      fractionDigits: React.PropTypes.number, // eslint-disable-line react/no-unused-prop-types
      groupingSymbol: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      negativeColor: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      negativeParens: React.PropTypes.bool, // eslint-disable-line react/no-unused-prop-types
      pattern: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      prefix: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      suffix: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    }),
  }),
  diffdata: React.PropTypes.shape({
    on: React.PropTypes.array, // eslint-disable-line react/no-unused-prop-types
    off: React.PropTypes.array, // eslint-disable-line react/no-unused-prop-types
  }),
};

Chart.defaultProps = {
  chartType: 'LineChart',
  rows: [],
  columns: [],
  options: {
    chart: {
      title: 'Chart Title',
      subtitle: 'Subtitle',
    },
    hAxis: { title: 'X Label' },
    vAxis: { title: 'Y Label' },
    width: '400px',
    height: '300px',
  },
  width: '400px',
  height: '300px',
  chartEvents: [],
  chartActions: null,
  data: null,
  legend_toggle: false,
  loadCharts: true,
  loader:
Rendering Chart
,
  chartPackages: ['corechart'],
  chartVersion: 'current',
  numberFormat: null,
  diffdata: null,
};
<|endoftext|>


<commit_before>/* eslint react/forbid-prop-types: "off" */
import React from 'react';
import Debug from 'debug';

import DEFAULT_COLORS from '../constants/DEFAULT_CHART_COLORS';
import googleChartLoader from './GoogleChartLoader';

const debug = new Debug('react-google-charts:Chart');

let uniqueID = 0;

const generateUniqueID = () => {
  uniqueID += 1;
  return `reactgooglegraph-${uniqueID}`;
};

export default class Chart extends React.Component {

  constructor(props) {
    debug('constructor', props);
    super(props);
    this.state = { graphID: props.graph_id || generateUniqueID() };
    this.chart = null;
    this.wrapper = null;
    this.hidden_columns = {};
    this.dataTable = [];

    this.debounce = this.debounce.bind(this);
    this.onResize = this.onResize.bind(this);
    this.drawChart = this.drawChart.bind(this);
    this.togglePoints = this.togglePoints.bind(this);
    this.buildDataTableFromProps = this.buildDataTableFromProps.bind(this);
    this.listenToChartEvents = this.listenToChartEvents.bind(this);
    this.addChartActions = this.addChartActions.bind(this);
    this.updateDataTable = this.updateDataTable.bind(this);
    this.onSelectToggle = this.onSelectToggle.bind(this);
    this.addSourceColumnTo = this.addSourceColumnTo.bind(this);
    this.restoreColorTo = this.restoreColorTo.bind(this);
    this.hideColumn = this.hideColumn.bind(this);
  }
  componentDidMount() {
    debug('componentDidMount');
    if (typeof window === 'undefined') {
      return;
    }
    if (this.props.loadCharts) {
      googleChartLoader.init(this.props.chartPackages, this.props.chartVersion).then(() => {
        this.drawChart();
      });
      window.addEventListener('resize', this.onResize);
      this.onResize = this.debounce(this.onResize, 200);
    } else {
      this.drawChart();
    }
  }

  componentDidUpdate() {
    debug('componentDidUpdate');
    if (!this.props.loadCharts) {
      this.drawChart();
    } else if (googleChartLoader.isLoading) {
      googleChartLoader.initPromise.then(() => {
        this.drawChart();
      });
    } else if (googleChartLoader.isLoaded) {
      this.drawChart();
    }
  }
  componentWillUnmount() {
    try {
      if (window) {
        if (window.google && window.google.visualization) {
          window.google.visualization.events.removeAllListeners(this.wrapper);
        }
        window.removeEventListener('resize', this.onResize);
      }
    } catch (err) {
      return;
    }
  }

  onResize() {
    debug('Chart::onResize');
    this.drawChart();
  }

  onSelectToggle() {
    debug('onSelectToggle');
    const selection = this.chart.getSelection();
    if (selection.length > 0) {
      if (selection[0].row == null) {
        const column = selection[0].column;
        this.togglePoints(column);
      }
    }
  }

  getColumnColor(columnIndex) {
    if (this.props.options.colors) {
      if (this.props.options.colors[columnIndex]) {
        return this.props.options.colors[columnIndex];
      }
    } else if (columnIndex in DEFAULT_COLORS) {
      return DEFAULT_COLORS[columnIndex];
    }
    return DEFAULT_COLORS[0];
  }

  buildDataTableFromProps() {
    debug('buildDataTableFromProps', this.props);

    if (this.props.diffdata) {
      const diffdata = this.props.diffdata;
      const oldData = window.google.visualization.arrayToDataTable(diffdata.old);
      const newData = window.google.visualization.arrayToDataTable(diffdata.new);
        // must take computeDiff from prototypes since not available with charts early in process
      const computeDiff = window.google.visualization[this.props.chartType].prototype.computeDiff;
      const chartDiff = computeDiff(oldData, newData);
      return chartDiff;
    }

    if (this.props.data === null && this.props.rows.length === 0 && !this.props.allowEmptyRows) {
      throw new Error("Can't build DataTable from rows and columns: rows array in props is empty");
    } else if (this.props.data === null && this.props.columns.length === 0) {
      throw new Error("Can't build DataTable from rows and columns: columns array in props is empty");
    }
    if (this.props.data !== null) {
      try {
        this.wrapper.setDataTable(this.props.data);
        const dataTable = this.wrapper.getDataTable();
        return dataTable;
      } catch (err) {
        // console.error('Failed to set DataTable from data props ! ', err);
        throw new Error('Failed to set DataTable from data props ! ', err);
      }
    }

    const dataTable = new window.google.visualization.DataTable();
    this.props.columns.forEach((column) => {
      dataTable.addColumn(column);
    });
    dataTable.addRows(this.props.rows);
    if (this.props.numberFormat) {
      const formatter = new window.google.visualization.NumberFormat(
        this.props.numberFormat.options
      );
      formatter.format(dataTable, this.props.numberFormat.column);
    }

    if (this.props.dateFormat) {
      const dateFormat = new window.google.visualization.DateFormat(
        this.props.dateFormat.options
      );
      this.props.dateFormat.columns.forEach((columnIdx) => {
        dateFormat.format(dataTable, columnIdx);
      });
    }

    return dataTable;
  }
  updateDataTable() {
    debug('updateDataTable');
    window.google.visualization.errors.removeAll(
      document.getElementById(this.wrapper.getContainerId())
    );
    this.dataTable.removeRows(0, this.dataTable.getNumberOfRows());
    this.dataTable.removeColumns(0, this.dataTable.getNumberOfColumns());
    this.dataTable = this.buildDataTableFromProps();
    return this.dataTable;
  }

  drawChart() {
    debug('drawChart', this);
    if (!this.wrapper) {
      const chartConfig = {
        chartType: this.props.chartType,
        options: this.props.options,
        containerId: this.state.graphID,
      };
      this.wrapper = new window.google.visualization.ChartWrapper(chartConfig);
      this.dataTable = this.buildDataTableFromProps();
      this.wrapper.setDataTable(this.dataTable);

      window.google.visualization.events.addOneTimeListener(this.wrapper, 'ready', () => {
        this.chart = this.wrapper.getChart();
        this.listenToChartEvents();
        this.addChartActions();
      });
    } else {
      this.updateDataTable();
      this.wrapper.setDataTable(this.dataTable);
       // this.wrapper.setChartType(this.props.chartType)
      this.wrapper.setOptions(this.props.options);
      if (this.wrapper.getChartType() !== this.props.chartType) {
        window.google.visualization.events.removeAllListeners(this.wrapper);
        this.wrapper.setChartType(this.props.chartType);
        const self = this;
        window.google.visualization.events.addOneTimeListener(this.wrapper, 'ready', () => {
          self.chart = self.wrapper.getChart();
          self.listenToChartEvents.call(self);
        });
      }
    }
    this.wrapper.draw();
  }

  addChartActions() {
    debug('addChartActions', this.props.chartActions);
    if (this.props.chartActions === null) {
      return;
    }
    this.props.chartActions.forEach((chartAction) => {
      this.chart.setAction({
        id: chartAction.id,
        text: chartAction.text,
        action: chartAction.action.bind(this, this.chart),
      });
    });
  }

  listenToChartEvents() {
    debug('listenToChartEvents', this.props.legend_toggle, this.props.chartEvents);
    if (this.props.legend_toggle) {
      window.google.visualization.events.addListener(
        this.wrapper,
        'select',
        this.onSelectToggle
      );
    }
    this.props.chartEvents.forEach((chartEvent) => {
      if (chartEvent.eventName === 'ready') {
        chartEvent.callback(this);
      } else {
        ((event) => {
          window.google.visualization.events.addListener(this.chart, event.eventName, (e) => {
            event.callback(this, e);
          });
        })(chartEvent);
      }
    });
  }

  buildColumnFromSourceData(columnIndex) {
    debug('buildColumnFromSourceData', columnIndex);
    return {
      label: this.dataTable.getColumnLabel(columnIndex),
      type: this.dataTable.getColumnType(columnIndex),
      sourceColumn: columnIndex,
    };
  }

  buildEmptyColumnFromSourceData(columnIndex) {
    debug('buildEmptyColumnFromSourceData', columnIndex);
    return {
      label: this.dataTable.getColumnLabel(columnIndex),
      type: this.dataTable.getColumnType(columnIndex),
      calc: () => null,
    };
  }
  addEmptyColumnTo(columns, columnIndex) {
    debug('addEmptyColumnTo', columns, columnIndex);
    const emptyColumn = this.buildEmptyColumnFromSourceData(columnIndex);
    columns.push(emptyColumn);
  }

  hideColumn(colors, columnIndex) {
    debug('hideColumn', colors, columnIndex);
    if (!this.isHidden(columnIndex)) {
      this.hidden_columns[columnIndex] = { color: this.getColumnColor(columnIndex - 1) };
    }
    colors.push('#CCCCCC');
  }
  addSourceColumnTo(columns, columnIndex) {
    debug('addSourceColumnTo', columns, columnIndex);
    const sourceColumn = this.buildColumnFromSourceData(columnIndex);
    columns.push(sourceColumn);
  }

  isHidden(columnIndex) {
    return this.hidden_columns[columnIndex] !== undefined;
  }
  restoreColorTo(colors, columnIndex) {
    debug('restoreColorTo', colors, columnIndex);
    debug('hidden_columns', this.hidden_columns);
    let previousColor;
    if (this.isHidden(columnIndex)) {
      previousColor = this.hidden_columns[columnIndex].color;
      delete this.hidden_columns[columnIndex];
    } else {
      previousColor = this.getColumnColor(columnIndex - 1);
    }
    if (columnIndex !== 0) {
      colors.push(previousColor);
    }
  }
  // eslint-disable-next-line class-methods-use-this
  debounce(func, wait) {
    let timeout;
    return function (...args) {
      const context = this;
      clearTimeout(timeout);
      timeout = setTimeout(() => func.apply(context, args), wait);
    };
  }
  togglePoints(column) {
    debug('togglePoints', column);
    const view = new window.google.visualization.DataView(this.wrapper.getDataTable());
    const columnCount = view.getNumberOfColumns();
    let colors = []; // eslint-disable-line prefer-const
    let columns = []; // eslint-disable-line prefer-const
    for (let i = 0; i < columnCount; i += 1) {
      // If user clicked on legend
      if (i === 0) {
        this.addSourceColumnTo(columns, i);
      } else if (i === column) {
        if (this.isHidden(i)) {
          this.addSourceColumnTo(columns, i);
          this.restoreColorTo(colors, i);
        } else {
          this.addEmptyColumnTo(columns, i);
          this.hideColumn(colors, i);
        }
      } else if (this.isHidden(i)) {
        this.addEmptyColumnTo(columns, i);
        this.hideColumn(colors, i);
      } else {
        this.addSourceColumnTo(columns, i);
        this.restoreColorTo(colors, i);
      }
    }
    view.setColumns(columns);
    this.props.options.colors = colors;
    this.chart.draw(view, this.props.options);
  }

  render() {
    debug('render', this.props, this.state);
    const divStyle = {
      height: this.props.height || this.props.options.height,
      width: this.props.width || this.props.options.width,
    };
    return (

        {this.props.loader ? this.props.loader : 'Rendering Chart...'}

    );
  }
}

Chart.propTypes = {
  graph_id: React.PropTypes.string,
  chartType: React.PropTypes.string,
  rows: React.PropTypes.arrayOf(React.PropTypes.array),
  columns: React.PropTypes.arrayOf(React.PropTypes.object),
  data: React.PropTypes.arrayOf(React.PropTypes.array),
  options: React.PropTypes.any,
  width: React.PropTypes.string,
  height: React.PropTypes.string,
  chartEvents: React.PropTypes.arrayOf(React.PropTypes.shape({
    // https://github.com/yannickcr/eslint-plugin-react/issues/819
    eventName: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    callback: React.PropTypes.func, // eslint-disable-line react/no-unused-prop-types
  })),
  chartActions: React.PropTypes.arrayOf(React.PropTypes.shape({
    id: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    text: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    action: React.PropTypes.func, // eslint-disable-line react/no-unused-prop-types
  })),
  loadCharts: React.PropTypes.bool,
  loader: React.PropTypes.node,
  legend_toggle: React.PropTypes.bool,
  allowEmptyRows: React.PropTypes.bool,
  chartPackages: React.PropTypes.arrayOf(React.PropTypes.string),
  chartVersion: React.PropTypes.string,
  numberFormat: React.PropTypes.shape({
    column: React.PropTypes.number, // eslint-disable-line react/no-unused-prop-types
    options: React.PropTypes.shape({
      decimalSymbol: React.PropTypes.string,  // eslint-disable-line react/no-unused-prop-types
      fractionDigits: React.PropTypes.number, // eslint-disable-line react/no-unused-prop-types
      groupingSymbol: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      negativeColor: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      negativeParens: React.PropTypes.bool, // eslint-disable-line react/no-unused-prop-types
      pattern: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      prefix: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      suffix: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    }),
  }),
  dateFormat: React.PropTypes.shape({
    columns: React.PropTypes.arrayOf(React.PropTypes.number), // eslint-disable-line react/no-unused-prop-types
    options: React.PropTypes.shape({
      formatType: React.PropTypes.string,  // eslint-disable-line react/no-unused-prop-types
      pattern: React.PropTypes.string,  // eslint-disable-line react/no-unused-prop-types
      timeZone: React.PropTypes.number,  // eslint-disable-line react/no-unused-prop-types
    }),
  }),
  diffdata: React.PropTypes.shape({
    on: React.PropTypes.array, // eslint-disable-line react/no-unused-prop-types
    off: React.PropTypes.array, // eslint-disable-line react/no-unused-prop-types
  }),
};

Chart.defaultProps = {
  chartType: 'LineChart',
  rows: [],
  columns: [],
  options: {
    chart: {
      title: 'Chart Title',
      subtitle: 'Subtitle',
    },
    hAxis: { title: 'X Label' },
    vAxis: { title: 'Y Label' },
    width: '400px',
    height: '300px',
  },
  width: '400px',
  height: '300px',
  chartEvents: [],
  chartActions: null,
  data: null,
  legend_toggle: false,
  allowEmptyRows: false,
  loadCharts: true,
  loader:
Rendering Chart
,
  chartPackages: ['corechart'],
  chartVersion: 'current',
  numberFormat: null,
  dateFormat: null,
  diffdata: null,
};
<commit_msg>Fix onResize event listener. Fixes #108 (#111)<commit_after>/* eslint react/forbid-prop-types: "off" */
import React from 'react';
import Debug from 'debug';

import DEFAULT_COLORS from '../constants/DEFAULT_CHART_COLORS';
import googleChartLoader from './GoogleChartLoader';

const debug = new Debug('react-google-charts:Chart');

let uniqueID = 0;

const generateUniqueID = () => {
  uniqueID += 1;
  return `reactgooglegraph-${uniqueID}`;
};

export default class Chart extends React.Component {

  constructor(props) {
    debug('constructor', props);
    super(props);
    this.state = { graphID: props.graph_id || generateUniqueID() };
    this.chart = null;
    this.wrapper = null;
    this.hidden_columns = {};
    this.dataTable = [];

    this.debounce = this.debounce.bind(this);
    this.onResize = this.onResize.bind(this);
    this.drawChart = this.drawChart.bind(this);
    this.togglePoints = this.togglePoints.bind(this);
    this.buildDataTableFromProps = this.buildDataTableFromProps.bind(this);
    this.listenToChartEvents = this.listenToChartEvents.bind(this);
    this.addChartActions = this.addChartActions.bind(this);
    this.updateDataTable = this.updateDataTable.bind(this);
    this.onSelectToggle = this.onSelectToggle.bind(this);
    this.addSourceColumnTo = this.addSourceColumnTo.bind(this);
    this.restoreColorTo = this.restoreColorTo.bind(this);
    this.hideColumn = this.hideColumn.bind(this);
  }
  componentDidMount() {
    debug('componentDidMount');
    if (typeof window === 'undefined') {
      return;
    }
    if (this.props.loadCharts) {
      googleChartLoader.init(this.props.chartPackages, this.props.chartVersion).then(() => {
        this.drawChart();
      });
      this.onResize = this.debounce(this.onResize, 200);
      window.addEventListener('resize', this.onResize);
    } else {
      this.drawChart();
    }
  }

  componentDidUpdate() {
    debug('componentDidUpdate');
    if (!this.props.loadCharts) {
      this.drawChart();
    } else if (googleChartLoader.isLoading) {
      googleChartLoader.initPromise.then(() => {
        this.drawChart();
      });
    } else if (googleChartLoader.isLoaded) {
      this.drawChart();
    }
  }
  componentWillUnmount() {
    try {
      if (window) {
        if (window.google && window.google.visualization) {
          window.google.visualization.events.removeAllListeners(this.wrapper);
        }
        window.removeEventListener('resize', this.onResize);
      }
    } catch (err) {
      return;
    }
  }

  onResize() {
    debug('Chart::onResize');
    this.drawChart();
  }

  onSelectToggle() {
    debug('onSelectToggle');
    const selection = this.chart.getSelection();
    if (selection.length > 0) {
      if (selection[0].row == null) {
        const column = selection[0].column;
        this.togglePoints(column);
      }
    }
  }

  getColumnColor(columnIndex) {
    if (this.props.options.colors) {
      if (this.props.options.colors[columnIndex]) {
        return this.props.options.colors[columnIndex];
      }
    } else if (columnIndex in DEFAULT_COLORS) {
      return DEFAULT_COLORS[columnIndex];
    }
    return DEFAULT_COLORS[0];
  }

  buildDataTableFromProps() {
    debug('buildDataTableFromProps', this.props);

    if (this.props.diffdata) {
      const diffdata = this.props.diffdata;
      const oldData = window.google.visualization.arrayToDataTable(diffdata.old);
      const newData = window.google.visualization.arrayToDataTable(diffdata.new);
        // must take computeDiff from prototypes since not available with charts early in process
      const computeDiff = window.google.visualization[this.props.chartType].prototype.computeDiff;
      const chartDiff = computeDiff(oldData, newData);
      return chartDiff;
    }

    if (this.props.data === null && this.props.rows.length === 0 && !this.props.allowEmptyRows) {
      throw new Error("Can't build DataTable from rows and columns: rows array in props is empty");
    } else if (this.props.data === null && this.props.columns.length === 0) {
      throw new Error("Can't build DataTable from rows and columns: columns array in props is empty");
    }
    if (this.props.data !== null) {
      try {
        this.wrapper.setDataTable(this.props.data);
        const dataTable = this.wrapper.getDataTable();
        return dataTable;
      } catch (err) {
        // console.error('Failed to set DataTable from data props ! ', err);
        throw new Error('Failed to set DataTable from data props ! ', err);
      }
    }

    const dataTable = new window.google.visualization.DataTable();
    this.props.columns.forEach((column) => {
      dataTable.addColumn(column);
    });
    dataTable.addRows(this.props.rows);
    if (this.props.numberFormat) {
      const formatter = new window.google.visualization.NumberFormat(
        this.props.numberFormat.options
      );
      formatter.format(dataTable, this.props.numberFormat.column);
    }

    if (this.props.dateFormat) {
      const dateFormat = new window.google.visualization.DateFormat(
        this.props.dateFormat.options
      );
      this.props.dateFormat.columns.forEach((columnIdx) => {
        dateFormat.format(dataTable, columnIdx);
      });
    }

    return dataTable;
  }
  updateDataTable() {
    debug('updateDataTable');
    window.google.visualization.errors.removeAll(
      document.getElementById(this.wrapper.getContainerId())
    );
    this.dataTable.removeRows(0, this.dataTable.getNumberOfRows());
    this.dataTable.removeColumns(0, this.dataTable.getNumberOfColumns());
    this.dataTable = this.buildDataTableFromProps();
    return this.dataTable;
  }

  drawChart() {
    debug('drawChart', this);
    if (!this.wrapper) {
      const chartConfig = {
        chartType: this.props.chartType,
        options: this.props.options,
        containerId: this.state.graphID,
      };
      this.wrapper = new window.google.visualization.ChartWrapper(chartConfig);
      this.dataTable = this.buildDataTableFromProps();
      this.wrapper.setDataTable(this.dataTable);

      window.google.visualization.events.addOneTimeListener(this.wrapper, 'ready', () => {
        this.chart = this.wrapper.getChart();
        this.listenToChartEvents();
        this.addChartActions();
      });
    } else {
      this.updateDataTable();
      this.wrapper.setDataTable(this.dataTable);
       // this.wrapper.setChartType(this.props.chartType)
      this.wrapper.setOptions(this.props.options);
      if (this.wrapper.getChartType() !== this.props.chartType) {
        window.google.visualization.events.removeAllListeners(this.wrapper);
        this.wrapper.setChartType(this.props.chartType);
        const self = this;
        window.google.visualization.events.addOneTimeListener(this.wrapper, 'ready', () => {
          self.chart = self.wrapper.getChart();
          self.listenToChartEvents.call(self);
        });
      }
    }
    this.wrapper.draw();
  }

  addChartActions() {
    debug('addChartActions', this.props.chartActions);
    if (this.props.chartActions === null) {
      return;
    }
    this.props.chartActions.forEach((chartAction) => {
      this.chart.setAction({
        id: chartAction.id,
        text: chartAction.text,
        action: chartAction.action.bind(this, this.chart),
      });
    });
  }

  listenToChartEvents() {
    debug('listenToChartEvents', this.props.legend_toggle, this.props.chartEvents);
    if (this.props.legend_toggle) {
      window.google.visualization.events.addListener(
        this.wrapper,
        'select',
        this.onSelectToggle
      );
    }
    this.props.chartEvents.forEach((chartEvent) => {
      if (chartEvent.eventName === 'ready') {
        chartEvent.callback(this);
      } else {
        ((event) => {
          window.google.visualization.events.addListener(this.chart, event.eventName, (e) => {
            event.callback(this, e);
          });
        })(chartEvent);
      }
    });
  }

  buildColumnFromSourceData(columnIndex) {
    debug('buildColumnFromSourceData', columnIndex);
    return {
      label: this.dataTable.getColumnLabel(columnIndex),
      type: this.dataTable.getColumnType(columnIndex),
      sourceColumn: columnIndex,
    };
  }

  buildEmptyColumnFromSourceData(columnIndex) {
    debug('buildEmptyColumnFromSourceData', columnIndex);
    return {
      label: this.dataTable.getColumnLabel(columnIndex),
      type: this.dataTable.getColumnType(columnIndex),
      calc: () => null,
    };
  }
  addEmptyColumnTo(columns, columnIndex) {
    debug('addEmptyColumnTo', columns, columnIndex);
    const emptyColumn = this.buildEmptyColumnFromSourceData(columnIndex);
    columns.push(emptyColumn);
  }

  hideColumn(colors, columnIndex) {
    debug('hideColumn', colors, columnIndex);
    if (!this.isHidden(columnIndex)) {
      this.hidden_columns[columnIndex] = { color: this.getColumnColor(columnIndex - 1) };
    }
    colors.push('#CCCCCC');
  }
  addSourceColumnTo(columns, columnIndex) {
    debug('addSourceColumnTo', columns, columnIndex);
    const sourceColumn = this.buildColumnFromSourceData(columnIndex);
    columns.push(sourceColumn);
  }

  isHidden(columnIndex) {
    return this.hidden_columns[columnIndex] !== undefined;
  }
  restoreColorTo(colors, columnIndex) {
    debug('restoreColorTo', colors, columnIndex);
    debug('hidden_columns', this.hidden_columns);
    let previousColor;
    if (this.isHidden(columnIndex)) {
      previousColor = this.hidden_columns[columnIndex].color;
      delete this.hidden_columns[columnIndex];
    } else {
      previousColor = this.getColumnColor(columnIndex - 1);
    }
    if (columnIndex !== 0) {
      colors.push(previousColor);
    }
  }
  // eslint-disable-next-line class-methods-use-this
  debounce(func, wait) {
    let timeout;
    return function (...args) {
      const context = this;
      clearTimeout(timeout);
      timeout = setTimeout(() => func.apply(context, args), wait);
    };
  }
  togglePoints(column) {
    debug('togglePoints', column);
    const view = new window.google.visualization.DataView(this.wrapper.getDataTable());
    const columnCount = view.getNumberOfColumns();
    let colors = []; // eslint-disable-line prefer-const
    let columns = []; // eslint-disable-line prefer-const
    for (let i = 0; i < columnCount; i += 1) {
      // If user clicked on legend
      if (i === 0) {
        this.addSourceColumnTo(columns, i);
      } else if (i === column) {
        if (this.isHidden(i)) {
          this.addSourceColumnTo(columns, i);
          this.restoreColorTo(colors, i);
        } else {
          this.addEmptyColumnTo(columns, i);
          this.hideColumn(colors, i);
        }
      } else if (this.isHidden(i)) {
        this.addEmptyColumnTo(columns, i);
        this.hideColumn(colors, i);
      } else {
        this.addSourceColumnTo(columns, i);
        this.restoreColorTo(colors, i);
      }
    }
    view.setColumns(columns);
    this.props.options.colors = colors;
    this.chart.draw(view, this.props.options);
  }

  render() {
    debug('render', this.props, this.state);
    const divStyle = {
      height: this.props.height || this.props.options.height,
      width: this.props.width || this.props.options.width,
    };
    return (

        {this.props.loader ? this.props.loader : 'Rendering Chart...'}

    );
  }
}

Chart.propTypes = {
  graph_id: React.PropTypes.string,
  chartType: React.PropTypes.string,
  rows: React.PropTypes.arrayOf(React.PropTypes.array),
  columns: React.PropTypes.arrayOf(React.PropTypes.object),
  data: React.PropTypes.arrayOf(React.PropTypes.array),
  options: React.PropTypes.any,
  width: React.PropTypes.string,
  height: React.PropTypes.string,
  chartEvents: React.PropTypes.arrayOf(React.PropTypes.shape({
    // https://github.com/yannickcr/eslint-plugin-react/issues/819
    eventName: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    callback: React.PropTypes.func, // eslint-disable-line react/no-unused-prop-types
  })),
  chartActions: React.PropTypes.arrayOf(React.PropTypes.shape({
    id: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    text: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    action: React.PropTypes.func, // eslint-disable-line react/no-unused-prop-types
  })),
  loadCharts: React.PropTypes.bool,
  loader: React.PropTypes.node,
  legend_toggle: React.PropTypes.bool,
  allowEmptyRows: React.PropTypes.bool,
  chartPackages: React.PropTypes.arrayOf(React.PropTypes.string),
  chartVersion: React.PropTypes.string,
  numberFormat: React.PropTypes.shape({
    column: React.PropTypes.number, // eslint-disable-line react/no-unused-prop-types
    options: React.PropTypes.shape({
      decimalSymbol: React.PropTypes.string,  // eslint-disable-line react/no-unused-prop-types
      fractionDigits: React.PropTypes.number, // eslint-disable-line react/no-unused-prop-types
      groupingSymbol: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      negativeColor: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      negativeParens: React.PropTypes.bool, // eslint-disable-line react/no-unused-prop-types
      pattern: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      prefix: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
      suffix: React.PropTypes.string, // eslint-disable-line react/no-unused-prop-types
    }),
  }),
  dateFormat: React.PropTypes.shape({
    columns: React.PropTypes.arrayOf(React.PropTypes.number), // eslint-disable-line react/no-unused-prop-types
    options: React.PropTypes.shape({
      formatType: React.PropTypes.string,  // eslint-disable-line react/no-unused-prop-types
      pattern: React.PropTypes.string,  // eslint-disable-line react/no-unused-prop-types
      timeZone: React.PropTypes.number,  // eslint-disable-line react/no-unused-prop-types
    }),
  }),
  diffdata: React.PropTypes.shape({
    on: React.PropTypes.array, // eslint-disable-line react/no-unused-prop-types
    off: React.PropTypes.array, // eslint-disable-line react/no-unused-prop-types
  }),
};

Chart.defaultProps = {
  chartType: 'LineChart',
  rows: [],
  columns: [],
  options: {
    chart: {
      title: 'Chart Title',
      subtitle: 'Subtitle',
    },
    hAxis: { title: 'X Label' },
    vAxis: { title: 'Y Label' },
    width: '400px',
    height: '300px',
  },
  width: '400px',
  height: '300px',
  chartEvents: [],
  chartActions: null,
  data: null,
  legend_toggle: false,
  allowEmptyRows: false,
  loadCharts: true,
  loader:
Rendering Chart
,
  chartPackages: ['corechart'],
  chartVersion: 'current',
  numberFormat: null,
  dateFormat: null,
  diffdata: null,
};
<|endoftext|>


<commit_before>var OpenConext = {};
OpenConext.Discover = function() {
    'use strict';
    this.searchBar = $('.mod-search-input');

    function hasDiscovery() {
        return 0 !== $('.mod-search-input').length;
    }

    function filterList(filterValue) {
        var filterElements = $('.mod-results a'),
            searchHeader = $('.mod-results header'),
            spinner = $('.mod-results .loading'),
            i, result, title;

        spinner.removeClass('hidden');

        filterValue = filterValue.toLowerCase();

        if (filterValue !== '') {
            searchHeader.hide();
        } else {
            searchHeader.show();
        }

        for (i = 0; i < filterElements.length; i++) {
            result = $(filterElements[i]);
            title = $(result).find('h3').text().toLowerCase();

            if (title.toString().indexOf(filterValue.toString()) > -1) {
                result.addClass('active');
                result.removeClass('hidden');
            } else {
                result.addClass('hidden');
                result.removeClass('active');
            }
        }

        spinner.addClass('hidden');

        // trigger the resize event to lazyload images
        $(window).trigger('resize');
    }

    function keyNavigation(e){
        var currentElement = $(document.activeElement),
            nextElement;

        // Press down
        if (e.which === 40) {
            if (currentElement.closest('.active').next().attr('type') === 'hidden') {
                nextElement = $('.list').find('.active:first');
                if (nextElement.length > 0) {
                    nextElement[0].focus();
                }
            } else if (currentElement.nextAll('.active:first')) {
                currentElement.nextAll('.active:first').focus();
            }
        }

        // Press up
        if (e.which === 38) {
            if (currentElement.prevAll('.active:first').length === 0) {
                $('input[type=search]').focus();
            } else if (currentElement.prevAll('.active:first')) {
                currentElement.prevAll('.active:first').focus();
            }
        }
    }

    if (!hasDiscovery()) {
        return this;
    }

    this.searchBar.on('input', function inputDetected(){
        filterList($(this).val());
    });

    var searchValue = $('input[type=search]').val();

    if (searchValue !== '') {
        filterList(searchValue);
    }

    $('.mod-results a').on('click', function selectIdp(){
       var selectedIdp = $(this).attr('data-idp');
        $('#form-idp').val(selectedIdp);
        $('form.mod-search').submit();
    });

    $('body').on('keydown', keyNavigation);
    $('img.logo').lazyload();
};

OpenConext.Tabs = function() {
    'use strict';
    this.activeTab = '';

    function hasTabs() {
      return 0 !== $('.mod-tabs').length;
    }

    function getActiveTab() {
        var firstTab = $('.mod-tabs .tab-target')[0],
            currentHash = window.location.hash,
            activeTab;

        if (currentHash !== '') {
            activeTab = currentHash;
        } else if (null !== firstTab) {
            activeTab = $(firstTab).attr('href');
        } else {
            activeTab = '';
        }

        return activeTab;
    }

    function setTab(activeTabId) {
        var tabPanels = $('.mod-tabpanel'),
            panel, panelId, i;

        for (i = 0; i < tabPanels.length; i++) {
            panel = $(tabPanels[i]);
            panelId = panel.attr('id');

            if (panelId === activeTabId) {
                panel.addClass('active');
            } else {
                panel.removeClass('active');
            }
        }
        $('.tab-target').removeClass('active');
        $('.tab-target[href="#'+activeTabId+'"]').addClass('active');
    }

    if (!hasTabs()) {
        return this;
    }

    this.activeTab = getActiveTab();

    if (this.activeTab !== '') {
        setTab(this.activeTab.replace('#', ''));
        window.addEventListener('hashchange', function tabActivated(){
            setTab(getActiveTab().replace('#', ''));
        }, false);
    }
};

$(function init(){
    new OpenConext.Tabs();
    new OpenConext.Discover();
});<commit_msg>Open first link on enter in searchbox<commit_after>var OpenConext = {};
OpenConext.Discover = function() {
    'use strict';
    this.searchBar = $('.mod-search-input');

    function hasDiscovery() {
        return 0 !== $('.mod-search-input').length;
    }

    function filterList(filterValue) {
        var filterElements = $('.mod-results a'),
            searchHeader = $('.mod-results header'),
            spinner = $('.mod-results .loading'),
            i, result, title;

        spinner.removeClass('hidden');

        filterValue = filterValue.toLowerCase();

        if (filterValue !== '') {
            searchHeader.hide();
        } else {
            searchHeader.show();
        }

        for (i = 0; i < filterElements.length; i++) {
            result = $(filterElements[i]);
            title = $(result).find('h3').text().toLowerCase();

            if (title.toString().indexOf(filterValue.toString()) > -1) {
                result.addClass('active');
                result.removeClass('hidden');
            } else {
                result.addClass('hidden');
                result.removeClass('active');
            }
        }

        spinner.addClass('hidden');

        // trigger the resize event to lazyload images
        $(window).trigger('resize');
    }

    function keyNavigation(e){
        var currentElement = $(document.activeElement),
            list = $('.list'),
            nextElement;

        // Press enter
        if (
          e.which === 13 &&
          currentElement.hasClass('mod-search-input') &&
          list.find('.active:first').length > 0
        ) {
          $('.list').find('.active:first').focus().trigger('click');
        }

        // Press down
        if (e.which === 40) {
            if (currentElement.closest('.active').next().attr('type') === 'hidden') {
                nextElement = $('.list').find('.active:first');
                if (nextElement.length > 0) {
                    nextElement[0].focus();
                }
            } else if (currentElement.nextAll('.active:first')) {
                currentElement.nextAll('.active:first').focus();
            }
        }

        // Press up
        if (e.which === 38) {
            if (currentElement.prevAll('.active:first').length === 0) {
                $('input[type=search]').focus();
            } else if (currentElement.prevAll('.active:first')) {
                currentElement.prevAll('.active:first').focus();
            }
        }
    }

    if (!hasDiscovery()) {
        return this;
    }

    this.searchBar.on('input', function inputDetected(){
        filterList($(this).val());
    });

    var searchValue = $('input[type=search]').val();

    if (searchValue !== '') {
        filterList(searchValue);
    }

    $('.mod-results a').on('click', function selectIdp(){
       var selectedIdp = $(this).attr('data-idp');
        $('#form-idp').val(selectedIdp);
        $('form.mod-search').submit();
    });

    $('body').on('keydown', keyNavigation);
    $('img.logo').lazyload();
};

OpenConext.Tabs = function() {
    'use strict';
    this.activeTab = '';

    function hasTabs() {
      return 0 !== $('.mod-tabs').length;
    }

    function getActiveTab() {
        var firstTab = $('.mod-tabs .tab-target')[0],
            currentHash = window.location.hash,
            activeTab;

        if (currentHash !== '') {
            activeTab = currentHash;
        } else if (null !== firstTab) {
            activeTab = $(firstTab).attr('href');
        } else {
            activeTab = '';
        }

        return activeTab;
    }

    function setTab(activeTabId) {
        var tabPanels = $('.mod-tabpanel'),
            panel, panelId, i;

        for (i = 0; i < tabPanels.length; i++) {
            panel = $(tabPanels[i]);
            panelId = panel.attr('id');

            if (panelId === activeTabId) {
                panel.addClass('active');
            } else {
                panel.removeClass('active');
            }
        }
        $('.tab-target').removeClass('active');
        $('.tab-target[href="#'+activeTabId+'"]').addClass('active');
    }

    if (!hasTabs()) {
        return this;
    }

    this.activeTab = getActiveTab();

    if (this.activeTab !== '') {
        setTab(this.activeTab.replace('#', ''));
        window.addEventListener('hashchange', function tabActivated(){
            setTab(getActiveTab().replace('#', ''));
        }, false);
    }
};

$(function init(){
    new OpenConext.Tabs();
    new OpenConext.Discover();
});<|endoftext|>