<commit_before>import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
import Toolbar from '@material-ui/core/Toolbar';
import IconButton from '@material-ui/core/IconButton';
import SearchIcon from '@material-ui/icons/Search';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import Grid from '@material-ui/core/Grid';
import Card from '@material-ui/core/Card';
import CardActionArea from '@material-ui/core/CardActionArea';
import CardContent from '@material-ui/core/CardContent';
import CardMedia from '@material-ui/core/CardMedia';
import Hidden from '@material-ui/core/Hidden';
import Link from '@material-ui/core/Link';
import Button from '@material-ui/core/Button';
import Divider from '@material-ui/core/Divider';
import Container from '@material-ui/core/Container';
import Markdown from './Markdown';
import post1 from './blog-post.1.md';
import post2 from './blog-post.2.md';
import post3 from './blog-post.3.md';

function Copyright() {
  return (
    <Typography variant="body2" color="textSecondary" align="center">
      {'Copyright © '}
      <Link color="inherit" href="https://material-ui.com/">
        Your Website
      </Link>{' '}
      {new Date().getFullYear()}
      {'.'}
    </Typography>
  );
}

const useStyles = makeStyles(theme => ({
  toolbar: {
    borderBottom: `1px solid ${theme.palette.divider}`,
  },
  toolbarTitle: {
    flex: 1,
  },
  toolbarSecondary: {
    justifyContent: 'space-between',
    overflowX: 'auto',
  },
  toolbarLink: {
    padding: theme.spacing(1),
    flexShrink: 0,
  },
  mainFeaturedPost: {
    position: 'relative',
    backgroundColor: theme.palette.grey[800],
    color: theme.palette.common.white,
    marginBottom: theme.spacing(4),
    backgroundImage: 'url(https://source.unsplash.com/user/erondu)',
    backgroundSize: 'cover',
    backgroundRepeat: 'no-repeat',
    backgroundPosition: 'center',
  },
  overlay: {
    position: 'absolute',
    top: 0,
    bottom: 0,
    right: 0,
    left: 0,
    backgroundColor: 'rgba(0,0,0,.3)',
  },
  mainFeaturedPostContent: {
    position: 'relative',
    padding: theme.spacing(3),
    [theme.breakpoints.up('md')]: {
      padding: theme.spacing(6),
      paddingRight: 0,
    },
  },
  mainGrid: {
    marginTop: theme.spacing(3),
  },
  card: {
    display: 'flex',
  },
  cardDetails: {
    flex: 1,
  },
  cardMedia: {
    width: 160,
  },
  markdown: {
    ...theme.typography.body2,
    padding: theme.spacing(3, 0),
  },
  sidebarAboutBox: {
    padding: theme.spacing(2),
    backgroundColor: theme.palette.grey[200],
  },
  sidebarSection: {
    marginTop: theme.spacing(3),
  },
  footer: {
    backgroundColor: theme.palette.background.paper,
    marginTop: theme.spacing(8),
    padding: theme.spacing(6, 0),
  },
}));

const sections = [
  'Technology',
  'Design',
  'Culture',
  'Business',
  'Politics',
  'Opinion',
  'Science',
  'Health',
  'Style',
  'Travel',
];

const featuredPosts = [
  {
    title: 'Featured post',
    date: 'Nov 12',
    description:
      'This is a wider card with supporting text below as a natural lead-in to additional content.',
  },
  {
    title: 'Post title',
    date: 'Nov 11',
    description:
      'This is a wider card with supporting text below as a natural lead-in to additional content.',
  },
];

const posts = [post1, post2, post3];

const archives = [
  'March 2020',
  'February 2020',
  'January 2020',
  'December 2019',
  'November 2019',
  'October 2019',
  'September 2019',
  'August 2019',
  'July 2019',
  'June 2019',
  'May 2019',
  'April 2019',
];

const social = ['GitHub', 'Twitter', 'Facebook'];

export default function Blog() {
  const classes = useStyles();

  return (
    <React.Fragment>
      <CssBaseline />
      <Container maxWidth="lg">
        <Toolbar className={classes.toolbar}>
          <Button size="small">Subscribe</Button>
          <Typography
            component="h2"
            variant="h5"
            color="inherit"
            align="center"
            noWrap
            className={classes.toolbarTitle}
          >
            Blog
          </Typography>
          <IconButton>
            <SearchIcon />
          </IconButton>
          <Button variant="outlined" size="small">
            Sign up
          </Button>
        </Toolbar>
        <Toolbar component="nav" variant="dense" className={classes.toolbarSecondary}>
          {sections.map(section => (
            <Link
              color="inherit"
              noWrap
              key={section}
              variant="body2"
              href="#"
              className={classes.toolbarLink}
            >
              {section}
            </Link>
          ))}
        </Toolbar>

          {/* Main featured post */}
          <Paper className={classes.mainFeaturedPost}>
            {/* Increase the priority of the hero background image */}
            {

            }

            <Grid container>
              <Grid item md={6}>

                  <Typography component="h1" variant="h3" color="inherit" gutterBottom>
                    Title of a longer featured blog post
                  </Typography>
                  <Typography variant="h5" color="inherit" paragraph>
                    Multiple lines of text that form the lede, informing new readers quickly and
                    efficiently about what's most interesting in this post's contents.
                  </Typography>
                  <Link variant="subtitle1" href="#">
                    Continue reading…
                  </Link>

              </Grid>
            </Grid>
          </Paper>
          {/* End main featured post */}
          {/* Sub featured posts */}
          <Grid container spacing={4} className={classes.cardGrid}>
            {featuredPosts.map(post => (
              <Grid item key={post.title} xs={12} md={6}>
                <CardActionArea component="a" href="#">
                  <Card className={classes.card}>

                      <CardContent>
                        <Typography component="h2" variant="h5">
                          {post.title}
                        </Typography>
                        <Typography variant="subtitle1" color="textSecondary">
                          {post.date}
                        </Typography>
                        <Typography variant="subtitle1" paragraph>
                          {post.description}
                        </Typography>
                        <Typography variant="subtitle1" color="primary">
                          Continue reading...
                        </Typography>
                      </CardContent>

                    <Hidden xsDown>
                      <CardMedia
                        className={classes.cardMedia}
                        image="https://source.unsplash.com/random"
                        title="Image title"
                      />
                    </Hidden>
                  </Card>
                </CardActionArea>
              </Grid>
            ))}
          </Grid>
          {/* End sub featured posts */}
          <Grid container spacing={5} className={classes.mainGrid}>
            {/* Main content */}
            <Grid item xs={12} md={8}>
              <Typography variant="h6" gutterBottom>
                From the Firehose
              </Typography>
              <Divider />
              {posts.map(post => (
                <Markdown className={classes.markdown} key={post.substring(0, 40)}>
                  {post}
                </Markdown>
              ))}
            </Grid>
            {/* End main content */}
            {/* Sidebar */}
            <Grid item xs={12} md={4}>
              <Paper elevation={0} className={classes.sidebarAboutBox}>
                <Typography variant="h6" gutterBottom>
                  About
                </Typography>
                <Typography>
                  Etiam porta sem malesuada magna mollis euismod. Cras mattis consectetur purus sit
                  amet fermentum. Aenean lacinia bibendum nulla sed consectetur.
                </Typography>
              </Paper>
              <Typography variant="h6" gutterBottom className={classes.sidebarSection}>
                Archives
              </Typography>
              {archives.map(archive => (
                <Link display="block" variant="body1" href="#" key={archive}>
                  {archive}
                </Link>
              ))}
              <Typography variant="h6" gutterBottom className={classes.sidebarSection}>
                Social
              </Typography>
              {social.map(network => (
                <Link display="block" variant="body1" href="#" key={network}>
                  {network}
                </Link>
              ))}
            </Grid>
            {/* End sidebar */}
          </Grid>

      </Container>
      {/* Footer */}

        <Container maxWidth="lg">
          <Typography variant="h6" align="center" gutterBottom>
            Footer
          </Typography>
          <Typography variant="subtitle1" align="center" color="textSecondary" component="p">
            Something here to give the footer a purpose!
          </Typography>
          <Copyright />
        </Container>

      {/* End footer */}
    </React.Fragment>
  );
}
<commit_msg>[docs] Remove an un-used className in template Blog (#17587)<commit_after>import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
import Toolbar from '@material-ui/core/Toolbar';
import IconButton from '@material-ui/core/IconButton';
import SearchIcon from '@material-ui/icons/Search';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import Grid from '@material-ui/core/Grid';
import Card from '@material-ui/core/Card';
import CardActionArea from '@material-ui/core/CardActionArea';
import CardContent from '@material-ui/core/CardContent';
import CardMedia from '@material-ui/core/CardMedia';
import Hidden from '@material-ui/core/Hidden';
import Link from '@material-ui/core/Link';
import Button from '@material-ui/core/Button';
import Divider from '@material-ui/core/Divider';
import Container from '@material-ui/core/Container';
import Markdown from './Markdown';
import post1 from './blog-post.1.md';
import post2 from './blog-post.2.md';
import post3 from './blog-post.3.md';

function Copyright() {
  return (
    <Typography variant="body2" color="textSecondary" align="center">
      {'Copyright © '}
      <Link color="inherit" href="https://material-ui.com/">
        Your Website
      </Link>{' '}
      {new Date().getFullYear()}
      {'.'}
    </Typography>
  );
}

const useStyles = makeStyles(theme => ({
  toolbar: {
    borderBottom: `1px solid ${theme.palette.divider}`,
  },
  toolbarTitle: {
    flex: 1,
  },
  toolbarSecondary: {
    justifyContent: 'space-between',
    overflowX: 'auto',
  },
  toolbarLink: {
    padding: theme.spacing(1),
    flexShrink: 0,
  },
  mainFeaturedPost: {
    position: 'relative',
    backgroundColor: theme.palette.grey[800],
    color: theme.palette.common.white,
    marginBottom: theme.spacing(4),
    backgroundImage: 'url(https://source.unsplash.com/user/erondu)',
    backgroundSize: 'cover',
    backgroundRepeat: 'no-repeat',
    backgroundPosition: 'center',
  },
  overlay: {
    position: 'absolute',
    top: 0,
    bottom: 0,
    right: 0,
    left: 0,
    backgroundColor: 'rgba(0,0,0,.3)',
  },
  mainFeaturedPostContent: {
    position: 'relative',
    padding: theme.spacing(3),
    [theme.breakpoints.up('md')]: {
      padding: theme.spacing(6),
      paddingRight: 0,
    },
  },
  mainGrid: {
    marginTop: theme.spacing(3),
  },
  card: {
    display: 'flex',
  },
  cardDetails: {
    flex: 1,
  },
  cardMedia: {
    width: 160,
  },
  markdown: {
    ...theme.typography.body2,
    padding: theme.spacing(3, 0),
  },
  sidebarAboutBox: {
    padding: theme.spacing(2),
    backgroundColor: theme.palette.grey[200],
  },
  sidebarSection: {
    marginTop: theme.spacing(3),
  },
  footer: {
    backgroundColor: theme.palette.background.paper,
    marginTop: theme.spacing(8),
    padding: theme.spacing(6, 0),
  },
}));

const sections = [
  'Technology',
  'Design',
  'Culture',
  'Business',
  'Politics',
  'Opinion',
  'Science',
  'Health',
  'Style',
  'Travel',
];

const featuredPosts = [
  {
    title: 'Featured post',
    date: 'Nov 12',
    description:
      'This is a wider card with supporting text below as a natural lead-in to additional content.',
  },
  {
    title: 'Post title',
    date: 'Nov 11',
    description:
      'This is a wider card with supporting text below as a natural lead-in to additional content.',
  },
];

const posts = [post1, post2, post3];

const archives = [
  'March 2020',
  'February 2020',
  'January 2020',
  'December 2019',
  'November 2019',
  'October 2019',
  'September 2019',
  'August 2019',
  'July 2019',
  'June 2019',
  'May 2019',
  'April 2019',
];

const social = ['GitHub', 'Twitter', 'Facebook'];

export default function Blog() {
  const classes = useStyles();

  return (
    <React.Fragment>
      <CssBaseline />
      <Container maxWidth="lg">
        <Toolbar className={classes.toolbar}>
          <Button size="small">Subscribe</Button>
          <Typography
            component="h2"
            variant="h5"
            color="inherit"
            align="center"
            noWrap
            className={classes.toolbarTitle}
          >
            Blog
          </Typography>
          <IconButton>
            <SearchIcon />
          </IconButton>
          <Button variant="outlined" size="small">
            Sign up
          </Button>
        </Toolbar>
        <Toolbar component="nav" variant="dense" className={classes.toolbarSecondary}>
          {sections.map(section => (
            <Link
              color="inherit"
              noWrap
              key={section}
              variant="body2"
              href="#"
              className={classes.toolbarLink}
            >
              {section}
            </Link>
          ))}
        </Toolbar>

          {/* Main featured post */}
          <Paper className={classes.mainFeaturedPost}>
            {/* Increase the priority of the hero background image */}
            {

            }

            <Grid container>
              <Grid item md={6}>

                  <Typography component="h1" variant="h3" color="inherit" gutterBottom>
                    Title of a longer featured blog post
                  </Typography>
                  <Typography variant="h5" color="inherit" paragraph>
                    Multiple lines of text that form the lede, informing new readers quickly and
                    efficiently about what's most interesting in this post's contents.
                  </Typography>
                  <Link variant="subtitle1" href="#">
                    Continue reading…
                  </Link>

              </Grid>
            </Grid>
          </Paper>
          {/* End main featured post */}
          {/* Sub featured posts */}
          <Grid container spacing={4}>
            {featuredPosts.map(post => (
              <Grid item key={post.title} xs={12} md={6}>
                <CardActionArea component="a" href="#">
                  <Card className={classes.card}>

                      <CardContent>
                        <Typography component="h2" variant="h5">
                          {post.title}
                        </Typography>
                        <Typography variant="subtitle1" color="textSecondary">
                          {post.date}
                        </Typography>
                        <Typography variant="subtitle1" paragraph>
                          {post.description}
                        </Typography>
                        <Typography variant="subtitle1" color="primary">
                          Continue reading...
                        </Typography>
                      </CardContent>

                    <Hidden xsDown>
                      <CardMedia
                        className={classes.cardMedia}
                        image="https://source.unsplash.com/random"
                        title="Image title"
                      />
                    </Hidden>
                  </Card>
                </CardActionArea>
              </Grid>
            ))}
          </Grid>
          {/* End sub featured posts */}
          <Grid container spacing={5} className={classes.mainGrid}>
            {/* Main content */}
            <Grid item xs={12} md={8}>
              <Typography variant="h6" gutterBottom>
                From the Firehose
              </Typography>
              <Divider />
              {posts.map(post => (
                <Markdown className={classes.markdown} key={post.substring(0, 40)}>
                  {post}
                </Markdown>
              ))}
            </Grid>
            {/* End main content */}
            {/* Sidebar */}
            <Grid item xs={12} md={4}>
              <Paper elevation={0} className={classes.sidebarAboutBox}>
                <Typography variant="h6" gutterBottom>
                  About
                </Typography>
                <Typography>
                  Etiam porta sem malesuada magna mollis euismod. Cras mattis consectetur purus sit
                  amet fermentum. Aenean lacinia bibendum nulla sed consectetur.
                </Typography>
              </Paper>
              <Typography variant="h6" gutterBottom className={classes.sidebarSection}>
                Archives
              </Typography>
              {archives.map(archive => (
                <Link display="block" variant="body1" href="#" key={archive}>
                  {archive}
                </Link>
              ))}
              <Typography variant="h6" gutterBottom className={classes.sidebarSection}>
                Social
              </Typography>
              {social.map(network => (
                <Link display="block" variant="body1" href="#" key={network}>
                  {network}
                </Link>
              ))}
            </Grid>
            {/* End sidebar */}
          </Grid>

      </Container>
      {/* Footer */}

        <Container maxWidth="lg">
          <Typography variant="h6" align="center" gutterBottom>
            Footer
          </Typography>
          <Typography variant="subtitle1" align="center" color="textSecondary" component="p">
            Something here to give the footer a purpose!
          </Typography>
          <Copyright />
        </Container>

      {/* End footer */}
    </React.Fragment>
  );
}
<|endoftext|>


<commit_before>
d3.rw = d3.rw || {};

(function() { //Utility Grid

	d3.rw.functionGrid = function(utilityFunction, xScale, yScale) {
		var grid = [];
		for(var xIndex = 0; xIndex <= xScale.domain()[1]; xIndex++) {
			grid.push([]);
			for(var yIndex = 0; yIndex <= yScale.domain()[1]; yIndex++) {
				grid[xIndex].push(utilityFunction(xScale(xIndex), yScale(yIndex)));
			}
		}
		return grid;
	};

})();

(function() { //Indifference Curve

	d3.rw.indifferenceCurve = function() {

        var grid,
            xScale,
            yScale,
            value,
            line = d3.svg.line(),
            container;

        function draw() {
            var path = [];

            var xEstimate, yEstimate;
            var prevX = 0, prevY;
            var width = grid.length, height = grid[0].length;

            for(var x = 1, maxX = width + 1; x < maxX; x++) {

                prevY = 0;

                for(var y = 1, maxY = height + 1; y < maxY; y++) {

                    // Check for x crossing
                    if (x < height && (grid[prevX][prevY] > value) !== (grid[x][prevY] > value)) {
                        var xEstimate = prevX + (value - grid[prevX][prevY]) / (grid[x][prevY] - grid[prevX][prevY]);
                        path.push([xScale(xEstimate), yScale(prevY)]);
                    }

                    // Check for y crossing
                    if(y < height && (grid[prevX][prevY] > value) !== (grid[prevX][y] > value)) {
                        var yEstimate = prevY + (value - grid[prevX][prevY]) / (grid[prevX][y] - grid[prevX][prevY]);
                        path.push([xScale(prevX), yScale(yEstimate)]);
                    }
                    prevY++;
                }

                prevX++;
            }

			path = path.sort(function(a, b) {
                return a[0] - b[0];
            });

            var curve = container.selectAll(".indifference-curve").data(path.length ? [path] : []);
            curve.enter()
				.append("path")
                .attr("class", "indifference-curve")
				.style("fill", "none");
            curve.attr("d", line)
			curve.exit().remove();
        }

        function indifferenceCurve(g) {

            container = g;

            if(value !== undefined) {
                draw();
            }

        }

        indifferenceCurve.grid = function(_) {
            if (!arguments.length) return grid;
            grid = _;
            return indifferenceCurve;
        };

        indifferenceCurve.xScale = function(_) {
            if (!arguments.length) return xScale;
            xScale = _;
            return indifferenceCurve;
        };

        indifferenceCurve.yScale = function(_) {
            if (!arguments.length) return yScale;
            yScale = _;
            return indifferenceCurve;
        };

        indifferenceCurve.value = function(_) {
            if (!arguments.length) return value;
            value = _;

            if(container !== undefined) {
                draw();
            }

            return indifferenceCurve;
        };

        return indifferenceCurve;
    };

})();

(function() {

    d3.rw.heatMap = function() {

        var grid,
            xScale,
            yScale,
            colorScale;

        function heatMap(g) {

            var data = [],
                sum,
				dx = (xScale(1) - xScale(0)) > 0 ? 0 : 1,
				dy = (yScale(1) - yScale(0)) > 0 ? 0 : 1;

            for(var x = 0; x < grid.length - 1; x++) {
                for(var y = 0; y < grid.length - 1; y++) {
                    sum = grid[x][y] + grid[x + 1][y] + grid[x + 1][y + 1] + grid[x][y + 1];
                    data.push({x: xScale(x + dx), y: yScale(y + dy), color: colorScale(sum / 4)});
                }
            }

			var width = Math.abs(xScale(1) - xScale(0)),
				height = Math.abs(yScale(1) - yScale(0));

            var rectangles = g.selectAll(".heatmap-rect").data(data);

            rectangles.enter()
                .append("rect")
                .attr("class", "heatmap-rect");

            rectangles
                .attr("x", function(d) { return d.x; })
                .attr("y", function(d) { return d.y; })
                .attr("width", width)
                .attr("height", height)
                .style("stroke", function(d) { return d.color; })
                .style("fill", function(d) { return d.color; });

            rectangles.exit().remove();
        }

        heatMap.grid = function(_) {
            if (!arguments.length) return grid;
            grid = _;
            return heatMap;
        };

        heatMap.xScale = function(_) {
            if (!arguments.length) return xScale;
            xScale = _;
            return heatMap;
        };

        heatMap.yScale = function(_) {
            if (!arguments.length) return yScale;
            yScale = _;
            return heatMap;
        };

        heatMap.colorScale = function(_) {
            if (!arguments.length) return colorScale;
            colorScale = _;
            return heatMap;
        };

        return heatMap;

    };

})();

/**
 * Up-samples or down-samples a given input array. It can be thought of as follows:
 * The values in the input array are connected with straight lines to form a continuous curve.
 * The value of this curve is then measured at c equally spaced intervals.
 * @param {Array<Any type that can be interpolated by d3>} input. The input array.
 * @param {Number} c. The number of samples to return.
 * @return {Array<typeof input>} An array of length c
 */
(function() {

	d3.rw.stretch = function(input, c) {
		if(input.length < 2) {
			throw("Cannot stretch fewer than 2 points");
		}

		c = c || (input.length + 1);

		if(c < 1) {
			throw("Cannot stretch to less than 1 point");
		}

		var inputIndex = d3.scale.linear().domain([0, c - 1]).range([0, input.length - 1]);

		var result = [];

		var i, a, interpolator;
		for(var o = 0; o < c - 1; o++) {
			i = inputIndex(o);
			a = Math.floor(i);
			interpolator = d3.interpolate(input[a], input[a + 1]);
			result.push(interpolator(i - a));
		}

		result.push(input[input.length - 1]);

		return result;
	};

})();
<commit_msg>added canvas based heatmap rendering<commit_after>
d3.rw = d3.rw || {};

(function() { //Utility Grid

	d3.rw.functionGrid = function(utilityFunction, xScale, yScale) {
		var grid = [];
		for(var xIndex = 0; xIndex <= xScale.domain()[1]; xIndex++) {
			grid.push([]);
			for(var yIndex = 0; yIndex <= yScale.domain()[1]; yIndex++) {
				grid[xIndex].push(utilityFunction(xScale(xIndex), yScale(yIndex)));
			}
		}
		return grid;
	};

})();

(function() { //Indifference Curve

	d3.rw.indifferenceCurve = function() {

        var grid,
            xScale,
            yScale,
            value,
            line = d3.svg.line(),
            container;

        function draw() {
            var path = [];

            var xEstimate, yEstimate;
            var prevX = 0, prevY;
            var width = grid.length, height = grid[0].length;

            for(var x = 1, maxX = width + 1; x < maxX; x++) {

                prevY = 0;

                for(var y = 1, maxY = height + 1; y < maxY; y++) {

                    // Check for x crossing
                    if (x < height && (grid[prevX][prevY] > value) !== (grid[x][prevY] > value)) {
                        var xEstimate = prevX + (value - grid[prevX][prevY]) / (grid[x][prevY] - grid[prevX][prevY]);
                        path.push([xScale(xEstimate), yScale(prevY)]);
                    }

                    // Check for y crossing
                    if(y < height && (grid[prevX][prevY] > value) !== (grid[prevX][y] > value)) {
                        var yEstimate = prevY + (value - grid[prevX][prevY]) / (grid[prevX][y] - grid[prevX][prevY]);
                        path.push([xScale(prevX), yScale(yEstimate)]);
                    }
                    prevY++;
                }

                prevX++;
            }

			path = path.sort(function(a, b) {
                return a[0] - b[0];
            });

            var curve = container.selectAll(".indifference-curve").data(path.length ? [path] : []);
            curve.enter()
				.append("path")
                .attr("class", "indifference-curve")
				.style("fill", "none");
            curve.attr("d", line)
			curve.exit().remove();
        }

        function indifferenceCurve(g) {

            container = g;

            if(value !== undefined) {
                draw();
            }

        }

        indifferenceCurve.grid = function(_) {
            if (!arguments.length) return grid;
            grid = _;
            return indifferenceCurve;
        };

        indifferenceCurve.xScale = function(_) {
            if (!arguments.length) return xScale;
            xScale = _;
            return indifferenceCurve;
        };

        indifferenceCurve.yScale = function(_) {
            if (!arguments.length) return yScale;
            yScale = _;
            return indifferenceCurve;
        };

        indifferenceCurve.value = function(_) {
            if (!arguments.length) return value;
            value = _;

            if(container !== undefined) {
                draw();
            }

            return indifferenceCurve;
        };

        return indifferenceCurve;
    };

})();

(function() {

    d3.rw.heatMap = function() {

        var grid,
            xScale,
            yScale,
            colorScale;

        function heatMap(g) {

            // create offscreen canvas
            var canvas = d3.select("#d3rw-canvas")
            if (canvas.empty()) {
                canvas = d3.select("body").append("canvas")
                    .attr("id", "d3rw-canvas")
                    .style("display", "none");
            }

            canvas.attr("width", grid.length).attr("height", grid.length);
            var context = canvas[0][0].getContext("2d");

            // write grid values to canvas
            for(var x = 0; x < grid.length - 1; x++) {
                for(var y = 0; y < grid.length - 1; y++) {
                    var sum = grid[x][y] + grid[x + 1][y] + grid[x + 1][y + 1] + grid[x][y + 1];
                    context.fillStyle = colorScale(sum / 4);
                    context.fillRect(x, grid.length-y-1, 1, 1);
                }
            }

            // copy canvas to svg
            // This will not work with multiple heatmaps in one document
            var canvasDataURL = canvas[0][0].toDataURL("image/png");

            var heatmapImage = g.select("#heatmap-image");
            if (heatmapImage.empty()) {
                heatmapImage = g.append("image")
                    .attr("id", "heatmap-image")
                    .attr("xlink:href", "")
                    .attr("width", xScale(grid.length)-xScale(0))
                    .attr("height", yScale(0)-yScale(grid.length));
            }
            heatmapImage.attr("xlink:href", canvasDataURL);

        }

        heatMap.grid = function(_) {
            if (!arguments.length) return grid;
            grid = _;
            return heatMap;
        };

        heatMap.xScale = function(_) {
            if (!arguments.length) return xScale;
            xScale = _;
            return heatMap;
        };

        heatMap.yScale = function(_) {
            if (!arguments.length) return yScale;
            yScale = _;
            return heatMap;
        };

        heatMap.colorScale = function(_) {
            if (!arguments.length) return colorScale;
            colorScale = _;
            return heatMap;
        };

        return heatMap;

    };

})();

/**
 * Up-samples or down-samples a given input array. It can be thought of as follows:
 * The values in the input array are connected with straight lines to form a continuous curve.
 * The value of this curve is then measured at c equally spaced intervals.
 * @param {Array<Any type that can be interpolated by d3>} input. The input array.
 * @param {Number} c. The number of samples to return.
 * @return {Array<typeof input>} An array of length c
 */
(function() {

	d3.rw.stretch = function(input, c) {
		if(input.length < 2) {
			throw("Cannot stretch fewer than 2 points");
		}

		c = c || (input.length + 1);

		if(c < 1) {
			throw("Cannot stretch to less than 1 point");
		}

		var inputIndex = d3.scale.linear().domain([0, c - 1]).range([0, input.length - 1]);

		var result = [];

		var i, a, interpolator;
		for(var o = 0; o < c - 1; o++) {
			i = inputIndex(o);
			a = Math.floor(i);
			interpolator = d3.interpolate(input[a], input[a + 1]);
			result.push(interpolator(i - a));
		}

		result.push(input[input.length - 1]);

		return result;
	};

})();
<|endoftext|>


<commit_before>module.exports = {
    all: {
        files: [
            { expand: true, src: ['config.json'], dest: 'dist' },
            { expand: true, src: ['javascript.json'], dest: 'dist' },
            { expand: true, cwd: 'src/config/locales/', src: ['**'], dest: 'dist/config/locales/' },
            { expand: true, cwd: 'src/images/', src: ['**'], dest: 'dist/images/', },
            { expand: true, cwd: 'src/flash/', src: ['**'], dest: 'dist/flash/' },
            { expand: true, cwd: 'src/templates/', src: ['**'], dest: 'dist/templates/' },
            { expand: true, cwd: 'src/css/external/jquery-ui-custom-theme/images/', src: ['**'], dest: 'dist/css/images' },
            { expand: true, cwd: 'src/css/external/jquery-ui-custom-theme/', src: ['*.css'], dest: 'dist/css/' },
            { expand: true, cwd: 'src/javascript/', src: ['**'], dest: 'dist/dev/src/javascript/' }
        ]
    }
};
<commit_msg>Update copy module config to include sitemap<commit_after>module.exports = {
    all: {
        files: [
            { expand: true, src: ['config.json'], dest: 'dist' },
            { expand: true, src: ['javascript.json'], dest: 'dist' },
            { expand: true, cwd: 'src/config/', src: ['**'], dest: 'dist/config/' },
            { expand: true, cwd: 'src/images/', src: ['**'], dest: 'dist/images/', },
            { expand: true, cwd: 'src/flash/', src: ['**'], dest: 'dist/flash/' },
            { expand: true, cwd: 'src/templates/', src: ['**'], dest: 'dist/templates/' },
            { expand: true, cwd: 'src/css/external/jquery-ui-custom-theme/images/', src: ['**'], dest: 'dist/css/images' },
            { expand: true, cwd: 'src/css/external/jquery-ui-custom-theme/', src: ['*.css'], dest: 'dist/css/' },
            { expand: true, cwd: 'src/javascript/', src: ['**'], dest: 'dist/dev/src/javascript/' }
        ]
    }
};
<|endoftext|>


<commit_before>'use strict';

angular.module('bbc.session', [])
    // Service for session handling
    .factory('bbcSession', function ($rootScope, $log, bbcTransport) {
        var pub = {};

        // save key value in session
        pub.setData = function (key, value, callback) {
            if (arguments.length === 3) {
                bbcTransport.rest('session/setData', {key: key, value: value}, callback);
            }
            else {
                $log.error('parameter error, required key, value and callback');
            }
        };

        // delete key value in session
        pub.deleteData = function (key, callback) {
            var data = {};

            if (arguments.length === 1) {
                callback = key;
                key = null;
            }
            else {
                data = {key: key};
            }

            bbcTransport.rest('session/deleteData', data, callback);
        };

        // get key value from session
        pub.getData = function (key, callback) {
            var data = {};

            if (arguments.length === 1) {
                callback = key;
            }
            else {
                data = {key: key};
            }

            bbcTransport.rest('session/getData', data, callback);
        };

        // check session and set activity time
        pub.getLastActivity = function (callback) {
            bbcTransport.rest('session/getLastActivity', {}, callback);
        };

        // check session and set activity time
        pub.setActivity = function (callback) {
            bbcTransport.rest('session/setActivity', {}, callback);
        };

        return pub;
    });<commit_msg>chore(session): update and refactor<commit_after>'use strict';

angular.module('bbc.session', [])
    // Service for session handling
    .factory('bbcSession', function ($rootScope, $log, transport) {
        var pub = {};

        // save key value in session
        pub.setData = function (key, value, callback) {
            if (arguments.length === 3) {
                transport.emit('api/session/setData', {key: key, value: value}, callback);
            }
            else {
                $log.error('parameter error, required key, value and callback');
            }
        };

        // delete key value in session
        pub.deleteData = function (key, callback) {
            var data = {};

            if (arguments.length === 1) {
                callback = key;
                key = null;
            }
            else {
                data = {key: key};
            }

            transport.emit('api/session/deleteData', data, callback);
        };

        // get key value from session
        pub.getData = function (key, callback) {
            var data = {};

            if (arguments.length === 1) {
                callback = key;
            }
            else {
                data = {key: key};
            }

            transport.emit('api/session/getData', data, callback);
        };

        // check session and set activity time
        pub.getLastActivity = function (callback) {
            transport.emit('api/session/getLastActivity', {}, callback);
        };

        // check session and set activity time
        pub.setActivity = function (callback) {
            transport.emit('api/session/setActivity', {}, callback);
        };

        return pub;
    });<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012 B3Partners B.V.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * Bookmark component
 * @author Meine Toonen
 */
Ext.define ("viewer.components.Bookmark",{
    extend: "viewer.components.Component",
    form: null,
    url: "",
    compUrl: "",
    baseUrl: "",
    shareUrls: null,
    imagePath: null,
    config:{
        title: null,
        titlebarIcon: null,
        tooltip: null,
        label: "",
        shareEmail: false,
        shareTwitter: false,
        shareLinkedIn: false,
        shareGooglePlus: false,
        shareFacebook: false,
        shareText: "I'd like to share this with #FlamingoMC: ",
        shareTitle: "Sharing"
    },
    constructor: function (conf){
        if(!Ext.isDefined(conf.details.height)) conf.details.height = 200;
        viewer.components.Bookmark.superclass.constructor.call(this, conf);
        this.initConfig(conf);

        imagePath=contextPath+"/viewer-html/components/resources/images/bookmark/"
        this.shareUrls ={
            email: "mailto:user@domain.com?subject=[title]&body=[text]%20[url]",
            twitter: "http://twitter.com/share?url=[url]&text=[text]",
            linkedin: "http://www.linkedin.com/shareArticle?mini=true&url=[url]&summary=[text]&title=[title]",
            googleplus: "https://plus.google.com/share?url=[url]&text=[text]",
            facebook: "https://www.facebook.com/sharer.php?u=[url]&text=[text]"
        }

        this.renderButton();
        this.loadWindow();

        return this;
    },
    renderButton: function() {
        var me = this;
        this.superclass.renderButton.call(this,{
            text: me.title,
            icon: me.titlebarIcon,
            tooltip: me.tooltip,
            label: me.label,
            handler: function() {
                me.showWindow();
            }
        });
    },
    loadWindow : function(){
        var socialButtons=[];
        if(this.shareEmail){
            socialButtons.push({
                xtype: 'button',
                margin: '10px 0px 0px 10px',
                //text: 'Email',
                icon: imagePath + 'email-16.png',
                listeners: {
                    click:{
                        scope: this,
                        fn: function(){
                            this.share(this.shareUrls["email"]);
                        }
                    }
                }
            });
        }if (this.shareTwitter){
            socialButtons.push({
                xtype: 'button',
                margin: '10px 0px 0px 10px',
                //text: 'Twitter',
                icon: imagePath + 'twitter-16.png',
                listeners: {
                    click:{
                        scope: this,
                        fn: function(){
                            this.share(this.shareUrls["twitter"]);
                        }
                    }
                }
            });
        }if (this.shareLinkedIn){
            socialButtons.push({
                xtype: 'button',
                margin: '10px 0px 0px 10px',
                //text: 'LinkedIn',
                icon: imagePath+"in-16.png",
                listeners: {
                    click:{
                        scope: this,
                        fn: function(){
                            this.share(this.shareUrls["linkedin"]);
                        }
                    }
                }
            });
        }if (this.shareGooglePlus){
            socialButtons.push({
                xtype: 'button',
                margin: '10px 0px 0px 10px',
                //text: 'Google+',
                icon: imagePath + 'gplus-16.png',
                listeners: {
                    click:{
                        scope: this,
                        fn: function(){
                            this.share(this.shareUrls["googleplus"]);
                        }
                    }
                }
            });
        }if (this.shareFacebook){
            socialButtons.push({
                xtype: 'button',
                margin: '10px 0px 0px 10px',
                //text: 'Facebook',
                icon: imagePath + 'fb-16.png',
                listeners: {
                    click:{
                        scope: this,
                        fn: function(){
                            this.share(this.shareUrls["facebook"]);
                        }
                    }
                }
            });
        }

        this.form = new Ext.form.FormPanel({
            frame: false,
            border: 0,
            width: '95%',
            margin: '0px 0px 0px 10px',
            padding: '5px',
            items: [{
                xtype: 'textfield',
                fieldLabel: 'Bookmark',
                name: 'bookmark',
                anchor: '100%',
                id: 'bookmark',
                value: this.url
            },{
                xtype: 'textfield',
                fieldLabel: 'Compact link',
                name: 'compactlink',
                anchor: '100%',
                id: 'compactlink'
            },{
                xtype: 'container',
                layout: {
                    type: 'hbox'
                },
                items: socialButtons
            },{
                xtype: 'button',
                componentCls: 'mobileLarge',
                margin: '10px 0px 0px 0px',
                text: 'Toevoegen aan favorieten',
                listeners: {
                    click:{
                        scope: this,
                        fn: this.addToFavorites
                    }
                }
            },{
                xtype: 'button',
                componentCls: 'mobileLarge',
                margin: '10px 0px 0px 10px',
                text: 'Sluiten',
                listeners: {
                    click:{
                        scope: this,
                        fn: this.hideWindow
                    }
                }
            }],
            renderTo: this.getContentDiv()
        });
    },
    showWindow : function(){
        var paramJSON = this.viewerController.getBookmarkUrl();
        var parameters = "";

        for ( var i = 0 ; i < paramJSON["params"].length ; i ++){
            var param = paramJSON["params"][i];
            if(param.name == 'url'){
                this.url = param.value;
                this.baseUrl = param.value;
            }else if(param.name == 'extent'){
                parameters += param.name +"=";
                var extent = param.value;
                parameters += extent.minx+","+extent.miny+","+extent.maxx+","+extent.maxy+"&";
            }else if(param.name == 'layers'){
                parameters += param.name +"=";
                var layers = param.value;
                for( var x = 0 ; x < layers.length ; x++){
                     parameters += layers[x];
                     if(x != (layers.length - 1)){
                         parameters += ",";
                     }
                }
                parameters +="&";
            }else if (param.name == 'levelOrder' && param.value.length > 0){
                parameters += param.name +"=";
                parameters += param.value.join(",");
                parameters +="&";
            }else if(param.name != 'selectedContent' && param.name != 'services' && param.name != 'appLayers' && param.name != "" && param.value != ""){
                parameters += param.name +"="+ param.value +"&";
            }
        }
        this.url += parameters;

        var me = this;
        Ext.create("viewer.Bookmark").createBookmark(
            Ext.JSON.encode(paramJSON),
            function(code){me.succesCompactUrl(code);},
            function(code){me.failureCompactUrl(code);}
        );
    },
    succesCompactUrl : function(code){
        this.compUrl = this.baseUrl+"bookmark="+code;
        this.form.getChildByElement("compactlink").setValue(this.compUrl);
        this.form.getChildByElement("bookmark").setValue(this.url);
        this.popup.show();
    },
    failureCompactUrl : function(code){
        // TODO: error message?
        this.viewerController.logger.error(code);
    },
    hideWindow : function(){
        this.popup.hide();
    },
    addToFavorites : function(){
        if(Ext.firefoxVersion != 0){
            alert("This browser doesn't support this function.");
        }else if(Ext.ieVersion != 0){
            window.external.AddFavorite(this.compUrl, this.title);
        }else if(Ext.chromeVersion != 0){
            alert("This browser doesn't support this function.");
        }else if(Ext.operaVersion != 0){
            alert("This browser doesn't support this function.");
        }else if(Ext.safariVersion != 0){
            alert("This browser doesn't support this function.");
        }
    },
    share: function (shareUrl){
        var url = ""+shareUrl;
        var bookmarkUrl = this.compUrl;
        if (!bookmarkUrl || bookmarkUrl == ""){
            bookmarkUrl=this.url;
        }
        if (url.indexOf("[url]")!=-1){
            url=url.replace("[url]",encodeURIComponent(bookmarkUrl));
        }
        if (url.indexOf("[text]")!=-1){
            url = url.replace("[text]",encodeURIComponent(this.shareText));
        }
        if (url.indexOf("[title]")!=-1){
            url = url.replace("[title]",encodeURIComponent(this.shareTitle));
        }
        console.log(url);
        window.open(url);
    },
    getExtComponents: function() {
        return [ this.form.getId() ];
    }
});<commit_msg>get the state of compnents and use them for the bookmark<commit_after>/*
 * Copyright (C) 2012 B3Partners B.V.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * Bookmark component
 * @author Meine Toonen
 */
Ext.define ("viewer.components.Bookmark",{
    extend: "viewer.components.Component",
    form: null,
    url: "",
    compUrl: "",
    baseUrl: "",
    shareUrls: null,
    imagePath: null,
    config:{
        title: null,
        titlebarIcon: null,
        tooltip: null,
        label: "",
        shareEmail: false,
        shareTwitter: false,
        shareLinkedIn: false,
        shareGooglePlus: false,
        shareFacebook: false,
        shareText: "I'd like to share this with #FlamingoMC: ",
        shareTitle: "Sharing"
    },
    constructor: function (conf){
        if(!Ext.isDefined(conf.details.height)) conf.details.height = 200;
        viewer.components.Bookmark.superclass.constructor.call(this, conf);
        this.initConfig(conf);

        imagePath=contextPath+"/viewer-html/components/resources/images/bookmark/"
        this.shareUrls ={
            email: "mailto:user@domain.com?subject=[title]&body=[text]%20[url]",
            twitter: "http://twitter.com/share?url=[url]&text=[text]",
            linkedin: "http://www.linkedin.com/shareArticle?mini=true&url=[url]&summary=[text]&title=[title]",
            googleplus: "https://plus.google.com/share?url=[url]&text=[text]",
            facebook: "https://www.facebook.com/sharer.php?u=[url]&text=[text]"
        }

        this.renderButton();
        this.loadWindow();

        return this;
    },
    renderButton: function() {
        var me = this;
        this.superclass.renderButton.call(this,{
            text: me.title,
            icon: me.titlebarIcon,
            tooltip: me.tooltip,
            label: me.label,
            handler: function() {
                me.showWindow();
            }
        });
    },
    loadWindow : function(){
        var socialButtons=[];
        if(this.shareEmail){
            socialButtons.push({
                xtype: 'button',
                margin: '10px 0px 0px 10px',
                //text: 'Email',
                icon: imagePath + 'email-16.png',
                listeners: {
                    click:{
                        scope: this,
                        fn: function(){
                            this.share(this.shareUrls["email"]);
                        }
                    }
                }
            });
        }if (this.shareTwitter){
            socialButtons.push({
                xtype: 'button',
                margin: '10px 0px 0px 10px',
                //text: 'Twitter',
                icon: imagePath + 'twitter-16.png',
                listeners: {
                    click:{
                        scope: this,
                        fn: function(){
                            this.share(this.shareUrls["twitter"]);
                        }
                    }
                }
            });
        }if (this.shareLinkedIn){
            socialButtons.push({
                xtype: 'button',
                margin: '10px 0px 0px 10px',
                //text: 'LinkedIn',
                icon: imagePath+"in-16.png",
                listeners: {
                    click:{
                        scope: this,
                        fn: function(){
                            this.share(this.shareUrls["linkedin"]);
                        }
                    }
                }
            });
        }if (this.shareGooglePlus){
            socialButtons.push({
                xtype: 'button',
                margin: '10px 0px 0px 10px',
                //text: 'Google+',
                icon: imagePath + 'gplus-16.png',
                listeners: {
                    click:{
                        scope: this,
                        fn: function(){
                            this.share(this.shareUrls["googleplus"]);
                        }
                    }
                }
            });
        }if (this.shareFacebook){
            socialButtons.push({
                xtype: 'button',
                margin: '10px 0px 0px 10px',
                //text: 'Facebook',
                icon: imagePath + 'fb-16.png',
                listeners: {
                    click:{
                        scope: this,
                        fn: function(){
                            this.share(this.shareUrls["facebook"]);
                        }
                    }
                }
            });
        }

        this.form = new Ext.form.FormPanel({
            frame: false,
            border: 0,
            width: '95%',
            margin: '0px 0px 0px 10px',
            padding: '5px',
            items: [{
                xtype: 'textfield',
                fieldLabel: 'Bookmark',
                name: 'bookmark',
                anchor: '100%',
                id: 'bookmark',
                value: this.url
            },{
                xtype: 'textfield',
                fieldLabel: 'Compact link',
                name: 'compactlink',
                anchor: '100%',
                id: 'compactlink'
            },{
                xtype: 'container',
                layout: {
                    type: 'hbox'
                },
                items: socialButtons
            },{
                xtype: 'button',
                componentCls: 'mobileLarge',
                margin: '10px 0px 0px 0px',
                text: 'Toevoegen aan favorieten',
                listeners: {
                    click:{
                        scope: this,
                        fn: this.addToFavorites
                    }
                }
            },{
                xtype: 'button',
                componentCls: 'mobileLarge',
                margin: '10px 0px 0px 10px',
                text: 'Sluiten',
                listeners: {
                    click:{
                        scope: this,
                        fn: this.hideWindow
                    }
                }
            }],
            renderTo: this.getContentDiv()
        });
    },
    showWindow : function(){
        var paramJSON = this.viewerController.getBookmarkUrl();
        var parameters = "";

        for ( var i = 0 ; i < paramJSON["params"].length ; i ++){
            var param = paramJSON["params"][i];
            if(param.name == 'url'){
                this.url = param.value;
                this.baseUrl = param.value;
            }else if(param.name == 'extent'){
                parameters += param.name +"=";
                var extent = param.value;
                parameters += extent.minx+","+extent.miny+","+extent.maxx+","+extent.maxy+"&";
            }else if(param.name == 'layers'){
                parameters += param.name +"=";
                var layers = param.value;
                for( var x = 0 ; x < layers.length ; x++){
                     parameters += layers[x];
                     if(x != (layers.length - 1)){
                         parameters += ",";
                     }
                }
                parameters +="&";
            }else if (param.name == 'levelOrder' && param.value.length > 0){
                parameters += param.name +"=";
                parameters += param.value.join(",");
                parameters +="&";
            }else if(param.name != 'selectedContent' && param.name != 'services' && param.name != 'appLayers' && param.name != "" && param.value != ""){
                parameters += param.name +"="+ param.value +"&";
            }
        }

        var componentParams="";
        //get all the params from components that need to be added to the bookmark
        var components = this.viewerController.getComponents();
        for (var i=0; i < components.length; i++){
            var state = components[i].getBookmarkState(false);
            if (!Ext.isEmpty(state)){
                componentParams+=encodeURIComponent(components[i].getName());
                componentParams+="=";
                componentParams+=encodeURIComponent(Ext.encode(state));
                componentParams+="&";
            }
        }
        this.url += parameters;
        if (componentParams.length!=0){
            this.url+=componentParams;
        }
        //get all the states of the components
        for (var i=0; i < components.length; i++){
            var state = components[i].getBookmarkState(true);
            if (!Ext.isEmpty(state)){
                paramJSON.params[components[i].getName()]=state;
            }
        }

        var me = this;
        Ext.create("viewer.Bookmark").createBookmark(
            Ext.JSON.encode(paramJSON),
            function(code){me.succesCompactUrl(code);},
            function(code){me.failureCompactUrl(code);}
        );
    },
    succesCompactUrl : function(code){
        this.compUrl = this.baseUrl+"bookmark="+code;
        this.form.getChildByElement("compactlink").setValue(this.compUrl);
        this.form.getChildByElement("bookmark").setValue(this.url);
        this.popup.show();
    },
    failureCompactUrl : function(code){
        // TODO: error message?
        this.viewerController.logger.error(code);
    },
    hideWindow : function(){
        this.popup.hide();
    },
    addToFavorites : function(){
        if(Ext.firefoxVersion != 0){
            alert("This browser doesn't support this function.");
        }else if(Ext.ieVersion != 0){
            window.external.AddFavorite(this.compUrl, this.title);
        }else if(Ext.chromeVersion != 0){
            alert("This browser doesn't support this function.");
        }else if(Ext.operaVersion != 0){
            alert("This browser doesn't support this function.");
        }else if(Ext.safariVersion != 0){
            alert("This browser doesn't support this function.");
        }
    },
    share: function (shareUrl){
        var url = ""+shareUrl;
        var bookmarkUrl = this.compUrl;
        if (!bookmarkUrl || bookmarkUrl == ""){
            bookmarkUrl=this.url;
        }
        if (url.indexOf("[url]")!=-1){
            url=url.replace("[url]",encodeURIComponent(bookmarkUrl));
        }
        if (url.indexOf("[text]")!=-1){
            url = url.replace("[text]",encodeURIComponent(this.shareText));
        }
        if (url.indexOf("[title]")!=-1){
            url = url.replace("[title]",encodeURIComponent(this.shareTitle));
        }
        console.log(url);
        window.open(url);
    },
    getExtComponents: function() {
        return [ this.form.getId() ];
    }
});<|endoftext|>


<commit_before>(function($) {

  $.setCookie = function(name, value, options){
    options = options || {};
    if (value === null) {
        value = '';
        options.expires = -1;
    }
    var expires = '';
    if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
        var date;
        if (typeof options.expires == 'number') {
            date = new Date();
            date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
        } else {
            date = options.expires;
        }
        expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
    }
    var path = options.path ? '; path=' + options.path : '';
    var domain = options.domain ? '; domain=' + options.domain : '';
    var secure = options.secure ? '; secure' : '';
    document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
  }

  $(function(){
    var top_nav = $('#top-nav');
    $('#body-content').css('margin-top', (top_nav.height() + 15) + 'px');

    if($("#g-theme-menu")){
      $('#g-theme-menu a').click(function(){
        var $el = $(this);
        var themeHref = $el.data('css-href');

        var topmenu = $('#top-nav .navbar-collapse');
        if(topmenu.data('bs.collapse')) topmenu.collapse('hide');

        var modal = $('

<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
'+
            'Loading theme

');
        modal.appendTo(document.body);

        modal.on('shown.bs.modal', function(){
          $.save_user_settings("site-theme", themeHref, function(){
            $.setCookie('_theme', themeHref);

            var $iframe = $("");
            $iframe.css("display", 'none');
            $iframe.appendTo(document.body);

            modal.on('hidden', function(e) {
              modal.remove();
            });

            $iframe.on("load", function () {
              $('#site-theme').attr('href', themeHref);

              setTimeout(function(){
                var nav_height = $('#top-nav').height();
                $('#body-content').animate({'margin-top': (nav_height + 15)}, 500, 'easeOutBounce');
              }, 500);

              modal.modal('hide');
              $iframe.remove();
            });

            $iframe.attr('src', window.location.href);
            $iframe.append('<!doctype>');
            $iframe.append('<link rel="stylesheet" href="'+themeHref+'" />');
            $iframe.append('');

            // option selected
            $el.parent().find("a").removeClass('active');
            $el.addClass('active');
          });
        });

        modal.modal().css(
            {
                'margin-top': function () {
                    return window.pageYOffset;
                }
            });
      })
    }
  });

})(jQuery);<commit_msg>Fix margin.<commit_after>(function($) {

  $.setCookie = function(name, value, options){
    options = options || {};
    if (value === null) {
        value = '';
        options.expires = -1;
    }
    var expires = '';
    if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
        var date;
        if (typeof options.expires == 'number') {
            date = new Date();
            date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
        } else {
            date = options.expires;
        }
        expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
    }
    var path = options.path ? '; path=' + options.path : '';
    var domain = options.domain ? '; domain=' + options.domain : '';
    var secure = options.secure ? '; secure' : '';
    document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
  }

  $(function(){
    var top_nav = $('#top-nav');
    $('#body-content').css('margin-top', (top_nav.height() + 20) + 'px');

    if($("#g-theme-menu")){
      $('#g-theme-menu a').click(function(){
        var $el = $(this);
        var themeHref = $el.data('css-href');

        var topmenu = $('#top-nav .navbar-collapse');
        if(topmenu.data('bs.collapse')) topmenu.collapse('hide');

        var modal = $('

<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
'+
            'Loading theme

');
        modal.appendTo(document.body);

        modal.on('shown.bs.modal', function(){
          $.save_user_settings("site-theme", themeHref, function(){
            $.setCookie('_theme', themeHref);

            var $iframe = $("");
            $iframe.css("display", 'none');
            $iframe.appendTo(document.body);

            modal.on('hidden', function(e) {
              modal.remove();
            });

            $iframe.on("load", function () {
              $('#site-theme').attr('href', themeHref);

              setTimeout(function(){
                var nav_height = $('#top-nav').height();
                $('#body-content').animate({'margin-top': (nav_height + 20)}, 500, 'easeOutBounce');
              }, 500);

              modal.modal('hide');
              $iframe.remove();
            });

            $iframe.attr('src', window.location.href);
            $iframe.append('<!doctype>');
            $iframe.append('<link rel="stylesheet" href="'+themeHref+'" />');
            $iframe.append('');

            // option selected
            $el.parent().find("a").removeClass('active');
            $el.addClass('active');
          });
        });

        modal.modal().css(
            {
                'margin-top': function () {
                    return window.pageYOffset;
                }
            });
      })
    }
  });

})(jQuery);<|endoftext|>


<commit_before>/******************************************************************************
DataBase workings
Author: mcdo7187@fredonia.edu
Date: 4/6/2015
Version: 0.0.5
*******************************************************************************/

/* Notes for Personal Use

$set - sets a value
$unset - unset a value
$inc - increment an integer
$push - append a value to an array
$pushAll- append several values to an array
$pull - remove a value from an arry
$pullAll - remove several values from an array
$bit - bitwise operators
$addToSet - adds a a value to a set if it doesn't exist.
$rename - renames a field

*/

// Grabbing Mongo for our project
var mongo = require('mongodb').MongoClient;

//Not username and password not included.
var uri = "mongodb://ds061158.mongolab.com:61158/gamething";

// Connecting to our hosted datatbase. Giving us an error message if we are unable to connect.
mongo.connect(uri, function(err, db){
	if(err){
		console.log("Error: Unable to connect to database.")
		return;
	}
});

// Inserting information to the users colleciton
db.users.insert(userSchema);

// Inserting information to the cards collection
db.cards.insert(cardSchema);

<commit_msg>Using mongoose instead of mongo connection<commit_after>/******************************************************************************
DataBase workings
Author: mcdo7187@fredonia.edu
Date: 4/6/2015
Version: 0.0.5
*******************************************************************************/

/* Notes for Personal Use

$set - sets a value
$unset - unset a value
$inc - increment an integer
$push - append a value to an array
$pushAll- append several values to an array
$pull - remove a value from an arry
$pullAll - remove several values from an array
$bit - bitwise operators
$addToSet - adds a a value to a set if it doesn't exist.
$rename - renames a field

*/

// Grabbing Mongoose for our project
var mongoose = require('mongoose')

//Note username and password not included.
var uri = "mongodb://ds061158.mongolab.com:61158/gamething";

// Connecting to our hosted datatbase. Giving us an error message if we are unable to connect.
mongoose.connect(uri, function(err, db){
	if(err){
		console.log("Error: Unable to connect to database.")
		return;
	}
});

// Inserting information to the users colleciton
db.users.insert(userSchema);

// Inserting information to the cards collection
db.cards.insert(cardSchema);

<|endoftext|>


<commit_before>var gulp = require('gulp'),
    $ = require('gulp-load-plugins')(),
    browserSync = require('browser-sync'),
    reload = browserSync.reload,
    wiredep = require('wiredep').stream,
    lazypipe = require('lazypipe'),
    pngcrush = require('imagemin-pngcrush'),
    runSequence = require('run-sequence'),
    args  = require('yargs').argv,
    folders = {
        app: 'app',
        tmp: '.tmp',
        dist: 'dist',
    },
    globs = {
        html: folders.app + '/*.html',
        templates: folders.app + '/*.jade',
        javascript: folders.app + '/**/*.js',
        coffee: folders.app + '/**/*.coffee',
        styles: folders.app + '/**/*.{scss,sass,css}',
        image: folders.app + '/**/*.{png,jpeg,jpg,gif,svg}'
    },
    ignore_dir = ['!' + folders.app + '/{bower_components,bower_components/**}'];

    // add ignores to globs
    for (glob in globs){
        globs[glob] = [globs[glob]].concat(ignore_dir);
    }

var isBuild = args.build !== undefined,
    isDev = !isBuild;

gulp.task('templates', function() {
    return gulp.src(globs.templates)
               .pipe($.plumber())
               .pipe($.if('*.jade', $.jade({pretty: true})))
               .pipe($.if(isDev, gulp.dest(folders.tmp)))
               .pipe($.if(isDev, reload({stream: true})))
               .pipe($.if(isBuild, $.useref.assets({
                    searchPath: [folders.app, folders.tmp]
               })))
               .pipe($.if(isBuild, $.useref.restore()))
               .pipe($.if(isBuild, $.useref()))
               .pipe($.if(isBuild, $.minifyHtml()))
               .pipe($.if(isBuild, gulp.dest(folders.dist)));
});

gulp.task('javascript', function() {
    return gulp.src(globs.javascript)
               .pipe($.plumber())
               .pipe($.jshint({lookup: true}))
               .pipe($.jshint.reporter('jshint-stylish'))
               //.pipe($.if(isBuild, $.uglify()))
               .pipe($.if(isBuild, gulp.dest(folders.dist)))
               .pipe($.if(isDev, reload({stream: true})));
});

gulp.task('coffee', function() {
    return gulp.src(globs.coffee)
               .pipe($.plumber())
               .pipe($.coffeelint())
               .pipe($.coffeelint.reporter())
               .pipe($.coffee())
               //.pipe($.if(isDev, gulp.dest(folders.tmp)))
               .pipe($.if(isBuild, $.uglify()))
               //.pipe($.if(isBuild, gulp.dest(folders.dist)))
               .pipe(gulp.dest(folders.tmp)) // Should solve issue with useref
               .pipe($.if(isDev, reload({stream: true})));
});

gulp.task('styles', function() {
    return gulp.src(globs.sass)
               .pipe($.plumber())
               .pipe($.if('*.{scss,sass}', $.sass({errLogToConsole: true}))) // Monkey patch for gulp-sass
               .pipe($.autoprefixer())
               //.pipe($.if(isDev, gulp.dest(folders.tmp)))
               .pipe($.if(isBuild, $.minifyCss()))
               //.pipe($.if(isBuild, gulp.dest(folders.dist)))
               .pipe(gulp.dest(folders.tmp)) // Should solve issue with useref
               .pipe($.if(isDev, reload({stream: true})));
});

gulp.task('wiredep', function() {
    return gulp.src([].concat(globs.jade, globs.html, globs.sass))
               .pipe($.plumber())
               .pipe(wiredep())
               .pipe($.if(isDev, reload({stream: true})))
               .pipe(gulp.dest(folders.app));
});

gulp.task('image', function() {
    return gulp.src(globs.image)
               .pipe($.plumber())
               .pipe($.if(isBuild, $.imagemin({
                progressive: true,
                svgoPlugins: [{removeViewBox: false}],
                use: [pngcrush()]
               })))
               .pipe($.if(isBuild, gulp.dest(folders.dist)))
});

gulp.task('watch', function() {
    for (var key in globs) {
        $.watch({glob: globs[key], name: key}, [key]);
    }

    $.watch({glob: 'bower.json', name: 'Wiredep'}, ['wiredep']);
});

gulp.task('connect', function() {
    browserSync({
        server: {
            baseDir: [folders.tmp, folders.app]
        }
    });
});

gulp.task('serve', ['watch', 'connect']);

gulp.task('default', ['wiredep', 'sass', 'coffee', 'javascript', 'html', 'templates', 'image']);

gulp.task('build', function(cb) {
    isBuild = true; isDev = false;
    runSequence(['coffee', 'sass'], ['wiredep', 'javascript', 'html', 'templates', 'image'], cb);
});
<commit_msg>Fixed miss<commit_after>var gulp = require('gulp'),
    $ = require('gulp-load-plugins')(),
    browserSync = require('browser-sync'),
    reload = browserSync.reload,
    wiredep = require('wiredep').stream,
    lazypipe = require('lazypipe'),
    pngcrush = require('imagemin-pngcrush'),
    runSequence = require('run-sequence'),
    args  = require('yargs').argv,
    folders = {
        app: 'app',
        tmp: '.tmp',
        dist: 'dist',
    },
    globs = {
        templates: folders.app + '/*.{jade,html}',
        javascript: folders.app + '/**/*.js',
        coffee: folders.app + '/**/*.coffee',
        styles: folders.app + '/**/*.{scss,sass,css}',
        image: folders.app + '/**/*.{png,jpeg,jpg,gif,svg}'
    },
    ignore_dir = ['!' + folders.app + '/{bower_components,bower_components/**}'];

    // add ignores to globs
    for (glob in globs){
        globs[glob] = [globs[glob]].concat(ignore_dir);
    }

var isBuild = args.build !== undefined,
    isDev = !isBuild;

gulp.task('templates', function() {
    return gulp.src(globs.templates)
               .pipe($.plumber())
               .pipe($.if('*.jade', $.jade({pretty: true})))
               .pipe($.if(isDev, gulp.dest(folders.tmp)))
               .pipe($.if(isDev, reload({stream: true})))
               .pipe($.if(isBuild, $.useref.assets({
                    searchPath: [folders.app, folders.tmp]
               })))
               .pipe($.if(isBuild, $.useref.restore()))
               .pipe($.if(isBuild, $.useref()))
               .pipe($.if(isBuild, $.minifyHtml()))
               .pipe($.if(isBuild, gulp.dest(folders.dist)));
});

gulp.task('javascript', function() {
    return gulp.src(globs.javascript)
               .pipe($.plumber())
               .pipe($.jshint({lookup: true}))
               .pipe($.jshint.reporter('jshint-stylish'))
               //.pipe($.if(isBuild, $.uglify()))
               .pipe($.if(isBuild, gulp.dest(folders.dist)))
               .pipe($.if(isDev, reload({stream: true})));
});

gulp.task('coffee', function() {
    return gulp.src(globs.coffee)
               .pipe($.plumber())
               .pipe($.coffeelint())
               .pipe($.coffeelint.reporter())
               .pipe($.coffee())
               //.pipe($.if(isDev, gulp.dest(folders.tmp)))
               .pipe($.if(isBuild, $.uglify()))
               //.pipe($.if(isBuild, gulp.dest(folders.dist)))
               .pipe(gulp.dest(folders.tmp)) // Should solve issue with useref
               .pipe($.if(isDev, reload({stream: true})));
});

gulp.task('styles', function() {
    return gulp.src(globs.sass)
               .pipe($.plumber())
               .pipe($.if('*.{scss,sass}', $.sass({errLogToConsole: true}))) // Monkey patch for gulp-sass
               .pipe($.autoprefixer())
               //.pipe($.if(isDev, gulp.dest(folders.tmp)))
               .pipe($.if(isBuild, $.minifyCss()))
               //.pipe($.if(isBuild, gulp.dest(folders.dist)))
               .pipe(gulp.dest(folders.tmp)) // Should solve issue with useref
               .pipe($.if(isDev, reload({stream: true})));
});

gulp.task('wiredep', function() {
    return gulp.src([].concat(globs.jade, globs.html, globs.sass))
               .pipe($.plumber())
               .pipe(wiredep())
               .pipe($.if(isDev, reload({stream: true})))
               .pipe(gulp.dest(folders.app));
});

gulp.task('image', function() {
    return gulp.src(globs.image)
               .pipe($.plumber())
               .pipe($.if(isBuild, $.imagemin({
                progressive: true,
                svgoPlugins: [{removeViewBox: false}],
                use: [pngcrush()]
               })))
               .pipe($.if(isBuild, gulp.dest(folders.dist)))
});

gulp.task('watch', function() {
    for (var key in globs) {
        $.watch({glob: globs[key], name: key}, [key]);
    }

    $.watch({glob: 'bower.json', name: 'Wiredep'}, ['wiredep']);
});

gulp.task('connect', function() {
    browserSync({
        server: {
            baseDir: [folders.tmp, folders.app]
        }
    });
});

gulp.task('serve', ['watch', 'connect']);

gulp.task('default', ['wiredep', 'sass', 'coffee', 'javascript', 'html', 'templates', 'image']);

gulp.task('build', function(cb) {
    isBuild = true; isDev = false;
    runSequence(['coffee', 'sass'], ['wiredep', 'javascript', 'html', 'templates', 'image'], cb);
});
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import moment from 'moment';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
import {latestNoteDateText} from '../../helpers/latestNoteDateText';
import {sortByGrade} from '../../helpers/SortHelpers';
import ExperimentalBanner from '../../components/ExperimentalBanner';
import DashboardHelpers from '../DashboardHelpers';
import StudentsTable from '../StudentsTable';
import DashboardBarChart from '../DashboardBarChart';
import DashRangeButtons from '../DashRangeButtons';

class SchoolDisciplineDashboard extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      startDate: DashboardHelpers.schoolYearStart(),
      selectedChart: 'location',
      selectedCategory: null
    };
    this.setStudentList = this.setStudentList.bind(this);
    this.resetStudentList = this.resetStudentList.bind(this);
    this.selectChart = this.selectChart.bind(this);
  }

  setStudentList(highchartsEvent) {
    this.setState({selectedCategory: highchartsEvent.point.category});
  }
  resetStudentList() {
    this.setState({selectedCategory: null});
  }
  selectChart(selection) {
    this.setState({selectedChart: selection.value, selectedCategory: null});
  }

  filterIncidentDates(incidentsArray) {
    return incidentsArray.filter((incident) => {
      return moment.utc(incident.occurred_at).isSameOrAfter(moment.utc(this.state.startDate));
    });
  }

  studentDisciplineIncidentCounts(incidentCategory) {
    let studentDisciplineIncidentCounts = {};
    const selectedChartData = this.getChartData(this.state.selectedChart);
    const incidents = incidentCategory ?
                      selectedChartData.disciplineIncidents[incidentCategory] :
                      this.filterIncidentDates(this.props.schoolDisciplineEvents);
    incidents.forEach((incident) => {
      studentDisciplineIncidentCounts[incident.student_id] = studentDisciplineIncidentCounts[incident.student_id] || 0;
      studentDisciplineIncidentCounts[incident.student_id]++;
    });
    return studentDisciplineIncidentCounts;
  }

  getChartData(selectedChart) {
    //This chart data is filtered based on the date selection and passed to the
    //student table and chart renders below
    const filteredEvents = this.filterIncidentDates(this.props.schoolDisciplineEvents);
    return {
      type: selectedChart,
      disciplineIncidents: _.groupBy(filteredEvents, selectedChart),
      title: "Incidents by " + selectedChart};
  }

  sortChartKeys(chartKeys) {
    switch(this.state.selectedChart) {
    case 'time': return this.sortedTimes(chartKeys);
    case 'day': return this.sortedDays(chartKeys);
    case 'grade': return this.sortedGrades(chartKeys);
    default: return this.sortedByIncidents(chartKeys);
    }
  }

  sortedDays(chartKeys) {
    return ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
  }

  sortedTimes(chartKeys) {
    //chartKeys will either contain a time like "4:00 pm", "10:00 am", or "Not Logged"
    return chartKeys.sort((a, b) => {
      if (a == "Not Logged") return -1;
      if (b == "Not Logged") return 1;
      return new Date('1970/01/01 ' + a) - new Date('1970/01/01 ' + b);
    });
  }

  sortedGrades(chartKeys) {
    return chartKeys.sort((a,b) => sortByGrade(a,b));
  }

  sortedByIncidents(chartKeys) {
    const chart = this.getChartData(this.state.selectedChart);
    return chartKeys.sort((a,b) => {
      return chart.disciplineIncidents[b].length - chart.disciplineIncidents[a].length;
    });
  }

  //For grades and classrooms, the students table should only show the relevant students
  groupStudents() {
    if (this.state.selectedChart === 'grade' && this.state.selectedCategory) {
      return this.props.dashboardStudents.filter(student => student.grade === this.state.selectedCategory);
    } else if (this.state.selectedChart === 'classroom' && this.state.selectedCategory) {
      return this.props.dashboardStudents.filter(student => student.homeroom_label === this.state.selectedCategory);
    } else return this.props.dashboardStudents;
  }

  render() {
    const selectedChart = this.getChartData(this.state.selectedChart);
    const chartOptions = [
      {value: 'location', label: 'Location'},
      {value: 'time', label: 'Time'},
      {value: 'classroom', label: 'Classroom'},
      {value: 'grade', label: 'Grade'},
      {value: 'day', label: 'Day'},
      {value: 'offense', label: 'Offense'},
    ];

    return(

      <ExperimentalBanner />

            {this.renderRangeSelector()}
            {this.renderStudentDisciplineTable()}

                Incidents by:

              <Select
                value={this.state.selectedChart}
                onChange={this.selectChart}
                options={chartOptions}
                style={styles.dropdown}
                clearable={false}
              />

           {this.renderDisciplineChart(selectedChart)}

    );
  }

  renderDisciplineChart(selectedChart) {
    const categories = this.sortChartKeys(Object.keys(selectedChart.disciplineIncidents));
    const seriesData = categories.map((type) => {
      if (!selectedChart.disciplineIncidents[type]) return [];
      const incidents = selectedChart.disciplineIncidents[type];
      return [type, incidents.length];
    });

    return (
        <DashboardBarChart
          id = "Discipline"
          categories = {{categories: categories}}
          seriesData = {seriesData}
          titleText = {null}
          measureText = {'Number of Incidents'}
          tooltip = {{
            pointFormat: 'Total incidents: {point.y}'}}
          onColumnClick = {this.setStudentList}
          onBackgroundClick = {this.resetStudentList}/>
    );
  }

  renderStudentDisciplineTable() {
    const students = this.groupStudents();
    const studentDisciplineIncidentCounts = this.studentDisciplineIncidentCounts(this.state.selectedCategory);
    let rows =[];
    students.forEach((student) => {
      rows.push({
        id: student.id,
        first_name: student.first_name,
        last_name: student.last_name,
        last_sst_date_text: latestNoteDateText(300, student.sst_notes),
        events: studentDisciplineIncidentCounts[student.id] || 0,
        grade: student.grade
      });
    });

    return (
      <StudentsTable
        rows = {rows}
        selectedCategory = {this.state.selectedCategory}
        incidentType={"Incidents"}
        resetFn={this.resetStudentList}/>
    );
  }

  renderRangeSelector() {
    const ninetyDaysAgo = moment.utc().subtract(90, 'days').format("YYYY-MM-DD");
    const fortyFiveDaysAgo = moment.utc().subtract(45, 'days').format("YYYY-MM-DD");
    const schoolYearStart = DashboardHelpers.schoolYearStart();
    return (

        <DashRangeButtons
          schoolYearFilter={() => this.setState({startDate: schoolYearStart, selectedRange: 'School Year'})}
          ninetyDayFilter={() => this.setState({startDate: ninetyDaysAgo, selectedRange: '90 Days'})}
          fortyFiveDayFilter={() => this.setState({startDate: fortyFiveDaysAgo, selectedRange: '45 Days'})}/>

    );
  }
}

SchoolDisciplineDashboard.propTypes = {
  dashboardStudents: PropTypes.array.isRequired,
  schoolDisciplineEvents: PropTypes.arrayOf(PropTypes.shape({
    student_id: PropTypes.number.isRequired, //ID of student involved in incident
    location: PropTypes.string, //Place where incident occurred
    time: PropTypes.string, //Time of day for incident - NULL if no specific time recorded
    classroom: PropTypes.string, //Name of student's homeroom teacher
    student_grade: PropTypes.string, //Grade of student
    day: PropTypes.string, //Day of week on which incident occurred
    offense: PropTypes.string, //Specific type of incident
    student_race: PropTypes.string, //Race of student
    occurred_at: PropTypes.string, //Date for incident, used in filtering specific date ranges
    last_sst_date_text: PropTypes.string //Date of last SST meeting
  })).isRequired
};

export default SchoolDisciplineDashboard;

const styles = {
  graphTitle: {
    display: 'flex',
    justifyContent: 'center',
    marginTop: '20px'
  },
  titleText: {
    fontSize: '18px',
    marginRight: '10px',
    alignSelf: 'center'
  },
  dropdown: {
    width: '200px'
  }
};
<commit_msg>refactor so chart and table have one source of data<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import moment from 'moment';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
import {latestNoteDateText} from '../../helpers/latestNoteDateText';
import {sortByGrade} from '../../helpers/SortHelpers';
import ExperimentalBanner from '../../components/ExperimentalBanner';
import DashboardHelpers from '../DashboardHelpers';
import StudentsTable from '../StudentsTable';
import DashboardBarChart from '../DashboardBarChart';
import DashRangeButtons from '../DashRangeButtons';

class SchoolDisciplineDashboard extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      startDate: DashboardHelpers.schoolYearStart(),
      selectedChart: 'location',
      selectedCategory: null
    };
    this.setStudentList = this.setStudentList.bind(this);
    this.resetStudentList = this.resetStudentList.bind(this);
    this.selectChart = this.selectChart.bind(this);
  }

  setStudentList(highchartsEvent) {
    this.setState({selectedCategory: highchartsEvent.point.category});
  }
  resetStudentList() {
    this.setState({selectedCategory: null});
  }
  selectChart(selection) {
    this.setState({selectedChart: selection.value, selectedCategory: null});
  }

  filterIncidentDates(incidentsArray) {
    return incidentsArray.filter((incident) => {
      return moment.utc(incident.occurred_at).isSameOrAfter(moment.utc(this.state.startDate));
    });
  }

  studentDisciplineIncidentCounts(incidents) {
    let studentDisciplineIncidentCounts = {};

    //if a user selects a category and then moves to a time range with no incidents within that category, return an empty object
    if (!incidents) return studentDisciplineIncidentCounts;
    incidents.forEach((incident) => {
      studentDisciplineIncidentCounts[incident.student_id] = studentDisciplineIncidentCounts[incident.student_id] || 0;
      studentDisciplineIncidentCounts[incident.student_id]++;
    });
    return studentDisciplineIncidentCounts;
  }

  sortChartKeys(groupedIncidents) {
    const chartKeys = Object.keys(groupedIncidents)
    switch(this.state.selectedChart) {
    case 'time': return this.sortedTimes(chartKeys);
    case 'day': return this.sortedDays(chartKeys);
    case 'grade': return this.sortedGrades(chartKeys);
    default: return this.sortedByIncidents(groupedIncidents); //because number of incidents in each category needed here
    }
  }

  sortedDays(chartKeys) {
    return ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
  }

  sortedTimes(chartKeys) {
    //chartKeys will either contain a time like "4:00 pm", "10:00 am", or "Not Logged"
    return chartKeys.sort((a, b) => {
      if (a == "Not Logged") return -1;
      if (b == "Not Logged") return 1;
      return new Date('1970/01/01 ' + a) - new Date('1970/01/01 ' + b);
    });
  }

  sortedGrades(chartKeys) {
    return chartKeys.sort((a,b) => sortByGrade(a,b));
  }

  sortedByIncidents(groupedIncidents) {
    const chartKeys = Object.keys(groupedIncidents);
    return chartKeys.sort((a,b) => {
      return groupedIncidents[b].length - groupedIncidents[a].length;
    });
  }

  //For grades and classrooms, the students table should only show the relevant students
  groupStudents() {
    if (this.state.selectedChart === 'grade' && this.state.selectedCategory) {
      return this.props.dashboardStudents.filter(student => student.grade === this.state.selectedCategory);
    } else if (this.state.selectedChart === 'classroom' && this.state.selectedCategory) {
      return this.props.dashboardStudents.filter(student => student.homeroom_label === this.state.selectedCategory);
    } else return this.props.dashboardStudents;
  }

  render() {
    const chartOptions = [
      {value: 'location', label: 'Location'},
      {value: 'time', label: 'Time'},
      {value: 'classroom', label: 'Classroom'},
      {value: 'grade', label: 'Grade'},
      {value: 'day', label: 'Day'},
      {value: 'offense', label: 'Offense'},
    ];
    const filteredIncidents = this.filterIncidentDates(this.props.schoolDisciplineEvents);
    const groupedIncidents = _.groupBy(filteredIncidents, this.state.selectedChart);

    return(

      <ExperimentalBanner />

            {this.renderRangeSelector()}
            {this.renderStudentDisciplineTable(filteredIncidents, groupedIncidents)}

                Incidents by:

              <Select
                value={this.state.selectedChart}
                onChange={this.selectChart}
                options={chartOptions}
                style={styles.dropdown}
                clearable={false}
              />

           {this.renderDisciplineChart(groupedIncidents)}

    );
  }

  renderDisciplineChart(incidents) {
    const categories = this.sortChartKeys(incidents);
    const seriesData = categories.map((type) => {
      if (!incidents[type]) return [];
      return [type, incidents[type].length];
    });

    return (
        <DashboardBarChart
          id = "Discipline"
          categories = {{categories: categories}}
          seriesData = {seriesData}
          titleText = {null}
          measureText = {'Number of Incidents'}
          tooltip = {{
            pointFormat: 'Total incidents: {point.y}'}}
          onColumnClick = {this.setStudentList}
          onBackgroundClick = {this.resetStudentList}/>
    );
  }

  renderStudentDisciplineTable(allIncidents, groupedIncidents) {
    const students = this.groupStudents();
    const studentDisciplineIncidentCounts = this.state.selectedCategory ? //if the user is looking at a subgroup of incidents
                                            this.studentDisciplineIncidentCounts(groupedIncidents[this.state.selectedCategory]) :
                                            this.studentDisciplineIncidentCounts(allIncidents);
    let rows =[];
    students.forEach((student) => {
      rows.push({
        id: student.id,
        first_name: student.first_name,
        last_name: student.last_name,
        last_sst_date_text: latestNoteDateText(300, student.sst_notes),
        events: studentDisciplineIncidentCounts[student.id] || 0,
        grade: student.grade
      });
    });

    return (
      <StudentsTable
        rows = {rows}
        selectedCategory = {this.state.selectedCategory}
        incidentType={"Incidents"}
        resetFn={this.resetStudentList}/>
    );
  }

  renderRangeSelector() {
    const ninetyDaysAgo = moment.utc().subtract(90, 'days').format("YYYY-MM-DD");
    const fortyFiveDaysAgo = moment.utc().subtract(45, 'days').format("YYYY-MM-DD");
    const schoolYearStart = DashboardHelpers.schoolYearStart();
    return (

        <DashRangeButtons
          schoolYearFilter={() => this.setState({startDate: schoolYearStart, selectedRange: 'School Year'})}
          ninetyDayFilter={() => this.setState({startDate: ninetyDaysAgo, selectedRange: '90 Days'})}
          fortyFiveDayFilter={() => this.setState({startDate: fortyFiveDaysAgo, selectedRange: '45 Days'})}/>

    );
  }
}

SchoolDisciplineDashboard.propTypes = {
  dashboardStudents: PropTypes.array.isRequired,
  schoolDisciplineEvents: PropTypes.arrayOf(PropTypes.shape({
    student_id: PropTypes.number.isRequired, //ID of student involved in incident
    location: PropTypes.string, //Place where incident occurred
    time: PropTypes.string, //Time of day for incident - NULL if no specific time recorded
    classroom: PropTypes.string, //Name of student's homeroom teacher
    student_grade: PropTypes.string, //Grade of student
    day: PropTypes.string, //Day of week on which incident occurred
    offense: PropTypes.string, //Specific type of incident
    student_race: PropTypes.string, //Race of student
    occurred_at: PropTypes.string, //Date for incident, used in filtering specific date ranges
    last_sst_date_text: PropTypes.string //Date of last SST meeting
  })).isRequired
};

export default SchoolDisciplineDashboard;

const styles = {
  graphTitle: {
    display: 'flex',
    justifyContent: 'center',
    marginTop: '20px'
  },
  titleText: {
    fontSize: '18px',
    marginRight: '10px',
    alignSelf: 'center'
  },
  dropdown: {
    width: '200px'
  }
};
<|endoftext|>


<commit_before>var alert_words_ui = (function () {

var exports = {};

function update_alert_word_status(status_text, is_error) {
    var alert_word_status = $('#alert_word_status');
    if (is_error) {
        alert_word_status.removeClass('alert-success').addClass('alert-danger');
    } else {
        alert_word_status.removeClass('alert-danger').addClass('alert-success');
    }
    alert_word_status.find('.alert_word_status_text').text(status_text);
    alert_word_status.show();
}

function update_alert_words() {
    var words = _.map($('.alert-word-item'), function (e) {
        return $(e).data('word').toString();
    });
    words = _.filter(words, function (word) {
        return word !== "";
    });
    channel.post({
        url: '/json/users/me/alert_words',
        idempotent: true,
        data: {alert_words: JSON.stringify(words)}});
}

function add_alert_word(word, event) {
    if ($.trim(word) === '') {
        update_alert_word_status(i18n.t("Alert words can't be empty!"), true);
        return;
    }
    var final_li = templates.render('alert_word_settings_item', {word: word, editing: false});

    var li = $(event.target).parents('li');
    li.replaceWith(final_li);

    var new_word = templates.render('alert_word_settings_item', {word: '', editing: true});
    var word_list = $('#alert_words_list');
    word_list.append(new_word);

    if (word_list.find('input').length > 0) {
        word_list.find('input').focus();
    }

    update_alert_words();
}

exports.set_up_alert_words = function () {
    // The settings page must be rendered before this function gets called.

    var word_list = $('#alert_words_list');
    _.each(alert_words.words, function (word) {
        var li = templates.render('alert_word_settings_item', {word: word});
        word_list.append(li);
    });
    var new_word = templates.render('alert_word_settings_item', {word: '', editing: true});
    word_list.append(new_word);

    $('#alert_words_list').on('click', '#create_alert_word_button', function (event) {
        var word = $('#create_alert_word_name').val();
        add_alert_word(word, event);
    });

    $('#alert_words_list').on('click', '.remove-alert-word', function (event) {
        var li = $(event.currentTarget).parents('li');
        li.remove();

        update_alert_words();
    });

    $('#alert_words_list').on('keypress', '#create_alert_word_name', function (event) {
        var key = event.which;
        // Handle enter (13) as "add".
        if (key === 13) {
            event.preventDefault();

            var word = $(event.target).val();
            add_alert_word(word, event);
        }
    });

    $('#alert-word-settings').on('click', '.close-alert-word-status', function (event) {
        event.preventDefault();
        var alert = $(event.currentTarget).parents('.alert');
        alert.hide();
    });
};

return exports;
}());

if (typeof module !== 'undefined') {
    module.exports = alert_words_ui;
}
<commit_msg>alert_words_ui: Add `render_alert_words_ui()` function.<commit_after>var alert_words_ui = (function () {

var exports = {};

exports.render_alert_words_ui = function () {
    var alert_words = page_params.alert_words;
    var word_list = $('#alert_words_list');

    word_list.find('.alert-word-item').remove();
    _.each(alert_words, function (alert_word) {
        var rendered_alert_word = templates.render('alert_word_settings_item',
                                                   {word: alert_word, editing: false});
        word_list.append(rendered_alert_word);
    });
    var new_alert_word_form = templates.render('alert_word_settings_item',
                                               {word: '', editing: true});
    word_list.append(new_alert_word_form);

    // Focus new alert word name text box.
    $('#create_alert_word_name').focus();
};

function update_alert_word_status(status_text, is_error) {
    var alert_word_status = $('#alert_word_status');
    if (is_error) {
        alert_word_status.removeClass('alert-success').addClass('alert-danger');
    } else {
        alert_word_status.removeClass('alert-danger').addClass('alert-success');
    }
    alert_word_status.find('.alert_word_status_text').text(status_text);
    alert_word_status.show();
}

function update_alert_words() {
    var words = _.map($('.alert-word-item'), function (e) {
        return $(e).data('word').toString();
    });
    words = _.filter(words, function (word) {
        return word !== "";
    });
    channel.post({
        url: '/json/users/me/alert_words',
        idempotent: true,
        data: {alert_words: JSON.stringify(words)}});
}

function add_alert_word(word, event) {
    if ($.trim(word) === '') {
        update_alert_word_status(i18n.t("Alert words can't be empty!"), true);
        return;
    }
    var final_li = templates.render('alert_word_settings_item', {word: word, editing: false});

    var li = $(event.target).parents('li');
    li.replaceWith(final_li);

    var new_word = templates.render('alert_word_settings_item', {word: '', editing: true});
    var word_list = $('#alert_words_list');
    word_list.append(new_word);

    if (word_list.find('input').length > 0) {
        word_list.find('input').focus();
    }

    update_alert_words();
}

exports.set_up_alert_words = function () {
    // The settings page must be rendered before this function gets called.

    exports.render_alert_words_ui();

    $('#alert_words_list').on('click', '#create_alert_word_button', function (event) {
        var word = $('#create_alert_word_name').val();
        add_alert_word(word, event);
    });

    $('#alert_words_list').on('click', '.remove-alert-word', function (event) {
        var li = $(event.currentTarget).parents('li');
        li.remove();

        update_alert_words();
    });

    $('#alert_words_list').on('keypress', '#create_alert_word_name', function (event) {
        var key = event.which;
        // Handle enter (13) as "add".
        if (key === 13) {
            event.preventDefault();

            var word = $(event.target).val();
            add_alert_word(word, event);
        }
    });

    $('#alert-word-settings').on('click', '.close-alert-word-status', function (event) {
        event.preventDefault();
        var alert = $(event.currentTarget).parents('.alert');
        alert.hide();
    });
};

return exports;
}());

if (typeof module !== 'undefined') {
    module.exports = alert_words_ui;
}
<|endoftext|>