<commit_before>var fs = require('fs');
var _s = require('underscore.string');
var util = require('util');
var ckan = require('ckan');
var request = require('request');
var async = require('async');
var _ = require('lodash');
var csvParser = require('csv-parser');

var BmDriverBase = require('benangmerah-driver-base');

var RDF_NS = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
var RDFS_NS = 'http://www.w3.org/2000/01/rdf-schema#';
var OWL_NS = 'http://www.w3.org/2002/07/owl#';
var XSD_NS = 'http://www.w3.org/2001/XMLSchema#';
var BM_NS = 'http://benangmerah.net/ontology/';
var PLACE_NS = 'http://benangmerah.net/place/idn/';
var BPS_NS = 'http://benangmerah.net/place/idn/bps/';
var GEO_NS = 'http://www.w3.org/2003/01/geo/wgs84_pos#';
var QB_NS = 'http://purl.org/linked-data/cube#';

function DataGoIdDriver() {}

util.inherits(DataGoIdDriver, BmDriverBase);

module.exports = DataGoIdDriver;

DataGoIdDriver.prototype.setOptions = function(options) {
  var self = this;

  self.options = _.extend({}, options);

  if (!self.options.ignoredFields) {
    // Ignore these fields, because we're using wilayah URIs
    // TODO have a flag to disable ignoring these fields
    self.options.ignoredFields =
      [ 'kode_provinsi', 'nama_provinsi', 'kode_kabkota', 'koordinat_provinsi',
        'koordinat_kabkota', 'nama_kabkota', 'tahun' ];
  }

  if (!self.options.ckanURL) {
    self.options.ckanURL = 'http://data.ukp.go.id/';
  }
  if (!self.options.base) {
    self.options.base = self.options.ckanURL + 'dataset/' +
                        self.options.datasetId + '#';
  }
  if (!self.options.dsd) {
    self.options.dsd = self.options.base + '_dsd';
  }

  self.info(self.options);
};

DataGoIdDriver.prototype.fetchFromCkan = function(callback) {
  var self = this;

  self.info('Fetching from CKAN...');

  var client = new ckan.Client(self.options.ckanURL);

  client.action('package_show', { id: self.options.datasetId },
    function(err, data) {
      if (err) {
        callback(err);
      }
      else {
        var resources = data.result.resources;
        // The CSV we want should be at index 0
        var firstResource = resources[0];

        self.csvUrl = firstResource.url;
        self.datasetMeta = data.result;
        self.org = data.result.organization;

        callback();
      }
    });
};

DataGoIdDriver.prototype.generateDSD = function(callback) {
  var self = this;

  if (!self.options.generateDSD) {
    return callback();
  }

  self.info('Generating data structure definition...');

  var base = self.options.base;
  var dsdUri = base + '_dsd';

  self.addTriple(dsdUri, RDF_NS + 'type',
                 QB_NS + 'DataStructureDefinition');
  self.addTriple('_:dsd-refArea', QB_NS + 'dimension', BM_NS + 'refArea');
  self.addTriple('_:dsd-refArea', QB_NS + 'order', '"1"');

  var order = 2;

  self.headerArray.forEach(function(header) {
    if (self.options.ignoredFields.indexOf(header) === -1) {
      self.addTriple(dsdUri, QB_NS + 'component', '_:dsd-' + header);
      self.addTriple('_:dsd-' + header, QB_NS + 'measure', base + header);
      self.addTriple('_:dsd-' + header, QB_NS + 'order', '"' + order + '"');

      self.addTriple(base + header, RDF_NS + 'type',
                     OWL_NS + 'DatatypeProperty');
      self.addTriple(base + header, RDF_NS + 'type', QB_NS + 'MeasureProperty');
      self.addTriple(base + header, RDFS_NS + 'label',
                     '"' + _s.titleize(_s.humanize(header)) + '"');
    }
    if (header === 'tahun') {
      self.addTriple(dsdUri, QB_NS + 'component', '_:dsd-' + header);
      self.addTriple('_:dsd-' + header, QB_NS + 'dimension',
                     BM_NS + 'refPeriod');
      self.addTriple('_:dsd-' + header, QB_NS + 'order', '"' + order + '"');
    }
  });

  callback();
};

DataGoIdDriver.prototype.initDataset = function(callback) {
  var self = this;

  self.info('Adding dataset definition...');

  var base = self.options.base;

  self.addTriple(base, RDF_NS + 'type', QB_NS + 'DataSet');
  self.addTriple(base, RDFS_NS + 'label',
                 '"' + self.datasetMeta.title + '"');
  self.addTriple(base, RDFS_NS + 'comment',
                 '"' + self.datasetMeta.notes + '"');
  self.addTriple(base, QB_NS + 'structure', self.options.dsd);

  callback();
};

DataGoIdDriver.prototype.addObservation = function(rowObject, idx) {
  var self = this;

  var base = self.options.base;

  var observationURI;
  if (self.options.generateObservationURI) {
    observationURI = self.options.generateObservationURI(rowObject, idx);
  }
  else {
    observationURI = base + '_' + idx;
  }

  self.addTriple(observationURI, RDF_NS + 'type', QB_NS + 'Observation');
  self.addTriple(observationURI, QB_NS + 'dataSet', base);
  self.addTriple(observationURI, BM_NS + 'refArea',
                 BPS_NS + rowObject.kode_kabkota);

  if (rowObject.tahun && self.options.ignoredFields.indexOf('tahun') !== -1) {
    self.addTriple(observationURI, BM_NS + 'refPeriod',
                   '"' + rowObject.tahun + '"^^<' + XSD_NS + 'gYear>');
  }

  Object.keys(rowObject).forEach(function(key) {
    if (self.options.ignoredFields.indexOf(key) === -1) {
      var value;
      if (self.options.transformValue) {
        value = self.options.transformValue(key, rowObject[key]);
      }
      else {
        value = '"' + rowObject[key] + '"';
      }
      self.addTriple(observationURI, base + key, value);
    }
  });
};

DataGoIdDriver.prototype.addObservations = function(callback) {
  var self = this;

  self.info('Fetching from CSV and adding observations...');

  var i = 0;
  request(self.csvUrl)
    .pipe(csvParser())
    .once('data', function(firstRow) {
      self.headerArray = Object.keys(firstRow);
    })
    .on('data', function(row) {
      self.addObservation(row, ++i);
    })
    .on('end', callback)
    .on('error', callback);
};

DataGoIdDriver.prototype.fetch = function() {
  var self = this;

  if (!self.options.datasetId) {
    self.error('No dataset was specified.');
    return;
  }

  async.waterfall([
    self.fetchFromCkan.bind(self),
    self.initDataset.bind(self),
    self.addObservations.bind(self),
    self.generateDSD.bind(self)
  ], function(err, params) {
    if (err) {
      self.error(err);
    }
    else {
      self.finish();
    }
  });
};

BmDriverBase.handleCLI();<commit_msg>DSD component ordering fix<commit_after>var fs = require('fs');
var _s = require('underscore.string');
var util = require('util');
var ckan = require('ckan');
var request = require('request');
var async = require('async');
var _ = require('lodash');
var csvParser = require('csv-parser');

var BmDriverBase = require('benangmerah-driver-base');

var RDF_NS = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
var RDFS_NS = 'http://www.w3.org/2000/01/rdf-schema#';
var OWL_NS = 'http://www.w3.org/2002/07/owl#';
var XSD_NS = 'http://www.w3.org/2001/XMLSchema#';
var BM_NS = 'http://benangmerah.net/ontology/';
var PLACE_NS = 'http://benangmerah.net/place/idn/';
var BPS_NS = 'http://benangmerah.net/place/idn/bps/';
var GEO_NS = 'http://www.w3.org/2003/01/geo/wgs84_pos#';
var QB_NS = 'http://purl.org/linked-data/cube#';

function DataGoIdDriver() {}

util.inherits(DataGoIdDriver, BmDriverBase);

module.exports = DataGoIdDriver;

DataGoIdDriver.prototype.setOptions = function(options) {
  var self = this;

  self.options = _.extend({}, options);

  if (!self.options.ignoredFields) {
    // Ignore these fields, because we're using wilayah URIs
    // TODO have a flag to disable ignoring these fields
    self.options.ignoredFields =
      [ 'kode_provinsi', 'nama_provinsi', 'kode_kabkota', 'koordinat_provinsi',
        'koordinat_kabkota', 'nama_kabkota', 'tahun' ];
  }

  if (!self.options.ckanURL) {
    self.options.ckanURL = 'http://data.ukp.go.id/';
  }
  if (!self.options.base) {
    self.options.base = self.options.ckanURL + 'dataset/' +
                        self.options.datasetId + '#';
  }
  if (!self.options.dsd) {
    self.options.dsd = self.options.base + '_dsd';
  }

  self.info(self.options);
};

DataGoIdDriver.prototype.fetchFromCkan = function(callback) {
  var self = this;

  self.info('Fetching from CKAN...');

  var client = new ckan.Client(self.options.ckanURL);

  client.action('package_show', { id: self.options.datasetId },
    function(err, data) {
      if (err) {
        callback(err);
      }
      else {
        var resources = data.result.resources;
        // The CSV we want should be at index 0
        var firstResource = resources[0];

        self.csvUrl = firstResource.url;
        self.datasetMeta = data.result;
        self.org = data.result.organization;

        callback();
      }
    });
};

DataGoIdDriver.prototype.generateDSD = function(callback) {
  var self = this;

  if (!self.options.generateDSD) {
    return callback();
  }

  self.info('Generating data structure definition...');

  var base = self.options.base;
  var dsdUri = base + '_dsd';

  self.addTriple(dsdUri, RDF_NS + 'type',
                 QB_NS + 'DataStructureDefinition');
  self.addTriple('_:dsd-refArea', QB_NS + 'dimension', BM_NS + 'refArea');
  self.addTriple('_:dsd-refArea', QB_NS + 'order', '"1"');

  var order = 2;

  self.headerArray.forEach(function(header) {
    if (self.options.ignoredFields.indexOf(header) === -1) {
      self.addTriple(dsdUri, QB_NS + 'component', '_:dsd-' + header);
      self.addTriple('_:dsd-' + header, QB_NS + 'measure', base + header);
      self.addTriple('_:dsd-' + header, QB_NS + 'order', '"' + order + '"');

      self.addTriple(base + header, RDF_NS + 'type',
                     OWL_NS + 'DatatypeProperty');
      self.addTriple(base + header, RDF_NS + 'type', QB_NS + 'MeasureProperty');
      self.addTriple(base + header, RDFS_NS + 'label',
                     '"' + _s.titleize(_s.humanize(header)) + '"');
    }
    if (header === 'tahun') {
      self.addTriple(dsdUri, QB_NS + 'component', '_:dsd-' + header);
      self.addTriple('_:dsd-' + header, QB_NS + 'dimension',
                     BM_NS + 'refPeriod');
      self.addTriple('_:dsd-' + header, QB_NS + 'order', '"' + order + '"');
    }

    ++order;
  });

  callback();
};

DataGoIdDriver.prototype.initDataset = function(callback) {
  var self = this;

  self.info('Adding dataset definition...');

  var base = self.options.base;

  self.addTriple(base, RDF_NS + 'type', QB_NS + 'DataSet');
  self.addTriple(base, RDFS_NS + 'label',
                 '"' + self.datasetMeta.title + '"');
  self.addTriple(base, RDFS_NS + 'comment',
                 '"' + self.datasetMeta.notes + '"');
  self.addTriple(base, QB_NS + 'structure', self.options.dsd);

  callback();
};

DataGoIdDriver.prototype.addObservation = function(rowObject, idx) {
  var self = this;

  var base = self.options.base;

  var observationURI;
  if (self.options.generateObservationURI) {
    observationURI = self.options.generateObservationURI(rowObject, idx);
  }
  else {
    observationURI = base + '_' + idx;
  }

  self.addTriple(observationURI, RDF_NS + 'type', QB_NS + 'Observation');
  self.addTriple(observationURI, QB_NS + 'dataSet', base);
  self.addTriple(observationURI, BM_NS + 'refArea',
                 BPS_NS + rowObject.kode_kabkota);

  if (rowObject.tahun && self.options.ignoredFields.indexOf('tahun') !== -1) {
    self.addTriple(observationURI, BM_NS + 'refPeriod',
                   '"' + rowObject.tahun + '"^^<' + XSD_NS + 'gYear>');
  }

  Object.keys(rowObject).forEach(function(key) {
    if (self.options.ignoredFields.indexOf(key) === -1) {
      var value;
      if (self.options.transformValue) {
        value = self.options.transformValue(key, rowObject[key]);
      }
      else {
        value = '"' + rowObject[key] + '"';
      }
      self.addTriple(observationURI, base + key, value);
    }
  });
};

DataGoIdDriver.prototype.addObservations = function(callback) {
  var self = this;

  self.info('Fetching from CSV and adding observations...');

  var i = 0;
  request(self.csvUrl)
    .pipe(csvParser())
    .once('data', function(firstRow) {
      self.headerArray = Object.keys(firstRow);
    })
    .on('data', function(row) {
      self.addObservation(row, ++i);
    })
    .on('end', callback)
    .on('error', callback);
};

DataGoIdDriver.prototype.fetch = function() {
  var self = this;

  if (!self.options.datasetId) {
    self.error('No dataset was specified.');
    return;
  }

  async.waterfall([
    self.fetchFromCkan.bind(self),
    self.initDataset.bind(self),
    self.addObservations.bind(self),
    self.generateDSD.bind(self)
  ], function(err, params) {
    if (err) {
      self.error(err);
    }
    else {
      self.finish();
    }
  });
};

BmDriverBase.handleCLI();<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import {
  withRouter,
  propTypes as routerPropTypes,
} from 'react-router-dom';

import { isEmpty, omit } from 'lodash';

import { grey300 } from 'material-ui/styles/colors';

import Paper from 'material-ui/Paper';
import FlatButton from 'material-ui/FlatButton';
import TextField from 'material-ui/TextField';
import Toggle from 'material-ui/Toggle';
import FavoriteIcon from 'material-ui/svg-icons/action/favorite';
import SaveIcon from 'material-ui/svg-icons/content/save';

import { loadListings } from './../actions/listings';
import { loadSeeks } from './../actions/seeks';
import { setExpandAll } from './../actions/ui';
import { postSavedSearch } from './../actions/savedSearches';
import { writeHistory } from '../helpers/query';

const mapStateToProps = state => ({
  displayMode: state.displayMode,
  query: state.currentQuery,
  leftDrawerVisible: state.leftDrawerVisible,
  expandAll: state.expandAll,
});

@withRouter
@connect(mapStateToProps)
export default class FilterBar extends Component {
  static propTypes = {
    ...routerPropTypes,
    dispatch: PropTypes.func.isRequired,
    history: PropTypes.shape({
      push: PropTypes.func.isRequired,
    }).isRequired,
    location: PropTypes.shape({
      pathname: PropTypes.string.isRequired,
    }).isRequired,
    displayMode: PropTypes.string.isRequired,
    expandAll: PropTypes.bool.isRequired,
    query: PropTypes.shape({
      isStarred: PropTypes.bool,
      query: PropTypes.string,
    }).isRequired,
    leftDrawerVisible: PropTypes.bool.isRequired,
    style: PropTypes.object, // eslint-disable-line react/forbid-prop-types
  }

  static defaultProps = {
    style: {
      display: 'flex',
      flexDirection: 'row',
      flexWrap: 'wrap',
      justifyContent: 'space-around',
      alignItems: 'center',
      alignContent: 'space-around',
    },
  };

  static styles = {
    base: {
      right: '0',
      position: 'fixed',
      paddingTop: '0.5em',
      paddingBottom: '0.5em',
      zIndex: '50',
    },
    priceField: {
      maxWidth: '6.5rem',
      width: '100%',
    },
  }

  state = {
    isStarred: false,
    minPrice: -1,
    maxPrice: -1,
  }

  componentWillMount() {
    this.setState({
      isStarred: this.props.query.isStarred,
      minPrice: this.props.query.minPrice / 100 || '',
      maxPrice: this.props.query.maxPrice / 100 || '',
    });
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      isStarred: nextProps.query.isStarred,
      minPrice: nextProps.query.minPrice / 100 || '',
      maxPrice: nextProps.query.maxPrice / 100 || '',
    });
  }

  handleFavorite = () => {
    const isStarred = !this.props.query.isStarred;
    const query = { isStarred };
    this.setState(query);
    switch (this.props.displayMode) {
      case 'seeks':
        this.props.dispatch(loadSeeks({ query }));
        break;
      case 'listings':
        this.props.dispatch(loadListings({ query }));
        break;
      default:
        break;
    }

    writeHistory({ query, history: this.props.history, location: this.props.location });
  }

  handleExpandAllToggle = (event, checked) => {
    this.props.dispatch(setExpandAll(checked));
  }

  handleMinChange = (event, minPrice) => {
    this.setState({ minPrice });
    const query = { minPrice: (minPrice === '') ? undefined : Math.floor(minPrice * 100) };
    switch (this.props.displayMode) {
      case 'seeks':
        this.props.dispatch(loadSeeks({ query }));
        break;
      case 'listings':
        this.props.dispatch(loadListings({ query }));
        break;
      default:
        break;
    }
  }

  handleMaxChange = (event, maxPrice) => {
    this.setState({ maxPrice });
    const query = { maxPrice: (maxPrice === '') ? undefined : Math.floor(maxPrice * 100) };
    switch (this.props.displayMode) {
      case 'seeks':
        this.props.dispatch(loadSeeks({ query }));
        break;
      case 'listings':
        this.props.dispatch(loadListings({ query }));
        break;
      default:
        break;
    }
  }

  handleOnBlur = () => {
    writeHistory(this.props);
  }

  handleWatchButtonTap = () => {
    this.props.dispatch(postSavedSearch(null, 'Successfully created saved search'));
    this.props.history.push('/savedsearches');
  }

  render() {
    const { query, leftDrawerVisible } = this.props;
    const styles = FilterBar.styles;

    return (
      <Paper
        style={{
          ...styles.base,
          left: leftDrawerVisible ? '20vw' : '0',
          ...this.props.style,
        }}
      >
        { (this.props.location.pathname === '/listings') &&
          <TextField
            hintText="Min Price"
            type="number"
            onChange={this.handleMinChange}
            onBlur={this.handleOnBlur}
            value={this.state.minPrice}
            style={styles.priceField}
            prefix="$"
            min="0"
            step="0.01"
          />
        }
        { (this.props.location.pathname === '/listings') &&
          <TextField
            hintText="Max Price"
            type="number"
            onChange={this.handleMaxChange}
            onBlur={this.handleOnBlur}
            value={this.state.maxPrice}
            style={styles.priceField}
            prefix="$"
            min="0"
            step="0.01"
          />
        }
        { (this.props.location.pathname === '/listings') &&
          <FlatButton
            secondary
            icon={<FavoriteIcon />}
            label="Favorites Only"
            backgroundColor={query.isStarred ? grey300 : 'transparent'}
            onTouchTap={this.handleFavorite}
          />
        }
        { (this.props.location.pathname === '/listings' || this.props.location.pathname === '/seeks') &&

            <Toggle
              label="Expand All"
              labelPosition="right"
              toggled={this.props.expandAll}
              onToggle={this.handleExpandAllToggle}
            />

        }
        { (this.props.location.pathname === '/listings') &&
          <FlatButton
            primary
            icon={<SaveIcon />}
            label="Watch Results"
            onTouchTap={this.handleWatchButtonTap}
            disabled={isEmpty(omit(query, ['isStarred', 'limit']))}
          />
        }
      </Paper>
    );
  }
}
<commit_msg>Fix my seeks/listings<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import {
  withRouter,
  propTypes as routerPropTypes,
} from 'react-router-dom';

import { isEmpty, omit } from 'lodash';

import { grey300 } from 'material-ui/styles/colors';

import Paper from 'material-ui/Paper';
import FlatButton from 'material-ui/FlatButton';
import TextField from 'material-ui/TextField';
import Toggle from 'material-ui/Toggle';
import FavoriteIcon from 'material-ui/svg-icons/action/favorite';
import SaveIcon from 'material-ui/svg-icons/content/save';

import { loadListings } from './../actions/listings';
import { loadSeeks } from './../actions/seeks';
import { setExpandAll } from './../actions/ui';
import { postSavedSearch } from './../actions/savedSearches';
import { writeHistory } from '../helpers/query';

const mapStateToProps = state => ({
  displayMode: state.displayMode,
  query: state.currentQuery,
  leftDrawerVisible: state.leftDrawerVisible,
  expandAll: state.expandAll,
});

@withRouter
@connect(mapStateToProps)
export default class FilterBar extends Component {
  static propTypes = {
    ...routerPropTypes,
    dispatch: PropTypes.func.isRequired,
    history: PropTypes.shape({
      push: PropTypes.func.isRequired,
    }).isRequired,
    location: PropTypes.shape({
      pathname: PropTypes.string.isRequired,
    }).isRequired,
    displayMode: PropTypes.string.isRequired,
    expandAll: PropTypes.bool.isRequired,
    query: PropTypes.shape({
      isStarred: PropTypes.bool,
      query: PropTypes.string,
    }).isRequired,
    leftDrawerVisible: PropTypes.bool.isRequired,
    style: PropTypes.object, // eslint-disable-line react/forbid-prop-types
  }

  static defaultProps = {
    style: {
      display: 'flex',
      flexDirection: 'row',
      flexWrap: 'wrap',
      justifyContent: 'space-around',
      alignItems: 'center',
      alignContent: 'space-around',
    },
  };

  static styles = {
    base: {
      right: '0',
      position: 'fixed',
      paddingTop: '0.5em',
      paddingBottom: '0.5em',
      zIndex: '50',
    },
    priceField: {
      maxWidth: '6.5rem',
      width: '100%',
    },
  }

  state = {
    isStarred: false,
    minPrice: -1,
    maxPrice: -1,
  }

  componentWillMount() {
    this.setState({
      isStarred: this.props.query.isStarred,
      minPrice: this.props.query.minPrice / 100 || '',
      maxPrice: this.props.query.maxPrice / 100 || '',
    });
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      isStarred: nextProps.query.isStarred,
      minPrice: nextProps.query.minPrice / 100 || '',
      maxPrice: nextProps.query.maxPrice / 100 || '',
    });
  }

  handleFavorite = () => {
    const isStarred = !this.props.query.isStarred;
    const query = { isStarred };
    this.setState(query);
    switch (this.props.displayMode) {
      case 'seeks':
        this.props.dispatch(loadSeeks({ query }));
        break;
      case 'listings':
        this.props.dispatch(loadListings({ query }));
        break;
      default:
        break;
    }

    writeHistory({ query, history: this.props.history, location: this.props.location });
  }

  handleExpandAllToggle = (event, checked) => {
    this.props.dispatch(setExpandAll(checked));
  }

  handleMinChange = (event, minPrice) => {
    this.setState({ minPrice });
    const query = { minPrice: (minPrice === '') ? undefined : Math.floor(minPrice * 100) };
    switch (this.props.displayMode) {
      case 'seeks':
        this.props.dispatch(loadSeeks({ query }));
        break;
      case 'listings':
        this.props.dispatch(loadListings({ query }));
        break;
      default:
        break;
    }
  }

  handleMaxChange = (event, maxPrice) => {
    this.setState({ maxPrice });
    const query = { maxPrice: (maxPrice === '') ? undefined : Math.floor(maxPrice * 100) };
    switch (this.props.displayMode) {
      case 'seeks':
        this.props.dispatch(loadSeeks({ query }));
        break;
      case 'listings':
        this.props.dispatch(loadListings({ query }));
        break;
      default:
        break;
    }
  }

  handleOnBlur = () => {
    writeHistory(this.props);
  }

  handleWatchButtonTap = () => {
    this.props.dispatch(postSavedSearch(null, 'Successfully created saved search'));
    this.props.history.push('/savedsearches');
  }

  render() {
    const { query, leftDrawerVisible } = this.props;
    const styles = FilterBar.styles;

    return (
      <Paper
        style={{
          ...styles.base,
          left: leftDrawerVisible ? '20vw' : '0',
          ...this.props.style,
        }}
      >
        { (this.props.location.pathname.startsWith('/listings')) &&
          <TextField
            hintText="Min Price"
            type="number"
            onChange={this.handleMinChange}
            onBlur={this.handleOnBlur}
            value={this.state.minPrice}
            style={styles.priceField}
            prefix="$"
            min="0"
            step="0.01"
          />
        }
        { (this.props.location.pathname.startsWith('/listings')) &&
          <TextField
            hintText="Max Price"
            type="number"
            onChange={this.handleMaxChange}
            onBlur={this.handleOnBlur}
            value={this.state.maxPrice}
            style={styles.priceField}
            prefix="$"
            min="0"
            step="0.01"
          />
        }
        { (this.props.location.pathname.startsWith('/listings')) &&
          <FlatButton
            secondary
            icon={<FavoriteIcon />}
            label="Favorites Only"
            backgroundColor={query.isStarred ? grey300 : 'transparent'}
            onTouchTap={this.handleFavorite}
          />
        }
        { (this.props.location.pathname.startsWith('/listings') || this.props.location.pathname.startsWith('/seeks')) &&

            <Toggle
              label="Expand All"
              labelPosition="right"
              toggled={this.props.expandAll}
              onToggle={this.handleExpandAllToggle}
            />

        }
        { (this.props.location.pathname === '/listings') &&
          <FlatButton
            primary
            icon={<SaveIcon />}
            label="Watch Results"
            onTouchTap={this.handleWatchButtonTap}
            disabled={isEmpty(omit(query, ['isStarred', 'limit']))}
          />
        }
      </Paper>
    );
  }
}
<|endoftext|>


<commit_before>
function deleteRepository(repositoryId, repositoryUrl) {
    var answer = confirm("Are you sure you want to remove this repository? \n " + repositoryUrl )
    if (answer){
    	AJS.$.ajax({
    	    url: BASE_URL+"/rest/bitbucket/1.0/repository/"+repositoryId,
    	    type: 'DELETE',
    	    success: function(result) {
    	    	window.location.reload();    	    }
    	});
    }
}

function toggleMoreFiles(target_div){
        AJS.$('#' + target_div).toggle();
        AJS.$('#see_more_' + target_div).toggle();
        AJS.$('#hide_more_' + target_div).toggle();
}

function retrieveSyncStatus() {
    AJS.$.getJSON(BASE_URL+"/rest/bitbucket/1.0/repositories", function (data) {
    	AJS.$.each(data.repositories, function(a,repo){
    		var syncStatusDiv = AJS.$('.gh_messages.repository'+repo.id+" .content");
    		var syncIconElement = AJS.$('.syncicon.repository'+repo.id);
    		var syncHtml;
    		var syncIcon;
    		if (repo.sync)
    		{
    			if(repo.sync.isFinished)
				{
    				syncIcon = "finished";
    				syncHtml = "Sync Finished:"
				} else
				{
    				syncIcon = "running";
					syncHtml = "Sync Running:"
				}
    			syncHtml = syncHtml + " Synchronized "+repo.sync.changesetCount+" changesets, found "+repo.sync.jiraCount+" matching JIRA issues";
    			if (repo.sync.error)
    			{
    				syncIcon = "error";
    				syncHtml = syncHtml + "
Sync Failed: "+repo.sync.error+"
";
    			}
    		} else
			{
				syncIcon = "";
    			syncHtml = "No information about sync available"
			}
    		syncIconElement.removeClass("finished").removeClass("running").removeClass("error").addClass(syncIcon);
    		syncStatusDiv.html(syncHtml);

    	});
    	window.setTimeout(retrieveSyncStatus, 4000)
    })
}

function forceSync(repositoryId){
	AJS.$.post(BASE_URL+"/rest/bitbucket/1.0/repository/"+repositoryId+"/sync");
	retrieveSyncStatus();
}

function submitFunction(a){
    var repositoryUrl = AJS.$("#url").val();
    var privateBbCredentialsVisible = AJS.$("#privateBbCredentialsVisible").val();
    //var atlToken = AJS.$("#atl_token").val();

    var requestUrl = BASE_URL+"/rest/bitbucket/1.0/urlinfo?repositoryUrl=" + encodeURIComponent(repositoryUrl)

    AJS.$("#aui-message-bar").empty();
	AJS.messages.generic({
	    title: "Working...",
	    body: "Trying to connect to the repository."
	});

	AJS.$.getJSON(requestUrl, function(data) {
        AJS.$("#isPrivate").val(data.isPrivate);

        if (data.repositoryType == "github")
    		AJS.$("#repoEntry").attr("action", BASE_URL+"/secure/admin/AddGithubRepository.jspa");
        else if (data.repositoryType == "bitbucket")
    		AJS.$("#repoEntry").attr("action", BASE_URL+"/secure/admin/AddBitbucketRepository.jspa");

        // todo: ak BB nema oauth
        if (privateBbCredentialsVisible == "false" && data.repositoryType == "bitbucket" && data.isPrivate) {
            AJS.$("#privateBbCredentialsVisible").val("true");
            AJS.$("#aui-message-bar").empty();

            var credentialsHtml = "
For private Bitbucket repository you have to add access credentials
" +
                "
" +
                "Username" +
                "<input type='text' name='bbUsername' id='bbUsername' value=''>
" +
                "
" +
                "Password" +
                "<input type='password' name='bbPassword' id='bbPassword' value=''>
";
            AJS.$("#bbCredentials").html(credentialsHtml);
        } else {
            AJS.$('#repoEntry').submit();
        }

//        if (data.repositoryType == "github")
//            var addRepositoryUrl = BASE_URL+"/secure/admin/AddGithubRepository.jspa?atl_token=" + atlToken;
//        else if (data.repositoryType == "bitbucket")
//            var addRepositoryUrl = BASE_URL+"/secure/admin/AddBitbucketRepository!default.jspa";
//        AJS.$.ajax({
//            type: "post",
//            url: addRepositoryUrl,
//            data: {},
//            success:function(response) {
//                var addRepositoryContent = AJS.$('#addRepositoryContent');
//                addRepositoryContent.html(response);
//        },
//        error: function(response) {
//            alert("failed");
//        }});

    }).error(function(a){
        AJS.$("#aui-message-bar").empty();
    	AJS.messages.error({
    	    title: "Error!",
    	    body: "The repository url ["+AJS.$("#url").val()+"] is incorrect or the repository is not responding."
    	});
    });
    return false;
}

function showAddRepoDetails(show)
{
    if (show)
    {
        AJS.$('#addRepositoryDetails').slideToggle();
        AJS.$('#linkRepositoryButton').hide();
    } else {
        AJS.$('#addRepositoryDetails').slideToggle();
        AJS.$("#bbCredentials").html("");
        AJS.$('#linkRepositoryButton').show();
    }

}

AJS.$(document).ready(function(){
	if(typeof init_repositories == 'function')
	{
		init_repositories();
	}

//	jQuery('#footer').addClass('footer');

})

<commit_msg>BBC-50 improved animation<commit_after>
function deleteRepository(repositoryId, repositoryUrl) {
    var answer = confirm("Are you sure you want to remove this repository? \n " + repositoryUrl )
    if (answer){
    	AJS.$.ajax({
    	    url: BASE_URL+"/rest/bitbucket/1.0/repository/"+repositoryId,
    	    type: 'DELETE',
    	    success: function(result) {
    	    	window.location.reload();    	    }
    	});
    }
}

function toggleMoreFiles(target_div){
        AJS.$('#' + target_div).toggle();
        AJS.$('#see_more_' + target_div).toggle();
        AJS.$('#hide_more_' + target_div).toggle();
}

function retrieveSyncStatus() {
    AJS.$.getJSON(BASE_URL+"/rest/bitbucket/1.0/repositories", function (data) {
    	AJS.$.each(data.repositories, function(a,repo){
    		var syncStatusDiv = AJS.$('.gh_messages.repository'+repo.id+" .content");
    		var syncIconElement = AJS.$('.syncicon.repository'+repo.id);
    		var syncHtml;
    		var syncIcon;
    		if (repo.sync)
    		{
    			if(repo.sync.isFinished)
				{
    				syncIcon = "finished";
    				syncHtml = "Sync Finished:"
				} else
				{
    				syncIcon = "running";
					syncHtml = "Sync Running:"
				}
    			syncHtml = syncHtml + " Synchronized "+repo.sync.changesetCount+" changesets, found "+repo.sync.jiraCount+" matching JIRA issues";
    			if (repo.sync.error)
    			{
    				syncIcon = "error";
    				syncHtml = syncHtml + "
Sync Failed: "+repo.sync.error+"
";
    			}
    		} else
			{
				syncIcon = "";
    			syncHtml = "No information about sync available"
			}
    		syncIconElement.removeClass("finished").removeClass("running").removeClass("error").addClass(syncIcon);
    		syncStatusDiv.html(syncHtml);

    	});
    	window.setTimeout(retrieveSyncStatus, 4000)
    })
}

function forceSync(repositoryId){
	AJS.$.post(BASE_URL+"/rest/bitbucket/1.0/repository/"+repositoryId+"/sync");
	retrieveSyncStatus();
}

function submitFunction(a){
    var repositoryUrl = AJS.$("#url").val();
    var privateBbCredentialsVisible = AJS.$("#privateBbCredentialsVisible").val();
    //var atlToken = AJS.$("#atl_token").val();

    var requestUrl = BASE_URL+"/rest/bitbucket/1.0/urlinfo?repositoryUrl=" + encodeURIComponent(repositoryUrl)

    AJS.$("#aui-message-bar").empty();
	AJS.messages.generic({
	    title: "Working...",
	    body: "Trying to connect to the repository."
	});

	AJS.$.getJSON(requestUrl, function(data) {
        AJS.$("#isPrivate").val(data.isPrivate);

        if (data.repositoryType == "github")
    		AJS.$("#repoEntry").attr("action", BASE_URL+"/secure/admin/AddGithubRepository.jspa");
        else if (data.repositoryType == "bitbucket")
    		AJS.$("#repoEntry").attr("action", BASE_URL+"/secure/admin/AddBitbucketRepository.jspa");

        // todo: ak BB nema oauth
        if (privateBbCredentialsVisible == "false" && data.repositoryType == "bitbucket" && data.isPrivate) {
            AJS.$("#privateBbCredentialsVisible").val("true");
            AJS.$("#aui-message-bar").empty();

            var credentialsHtml = "
For private Bitbucket repository you have to add access credentials
" +
                "
" +
                "Username" +
                "<input type='text' name='bbUsername' id='bbUsername' value=''>
" +
                "
" +
                "Password" +
                "<input type='password' name='bbPassword' id='bbPassword' value=''>
";
            AJS.$("#bbCredentials").html(credentialsHtml);
        } else {
            AJS.$('#repoEntry').submit();
        }

//        if (data.repositoryType == "github")
//            var addRepositoryUrl = BASE_URL+"/secure/admin/AddGithubRepository.jspa?atl_token=" + atlToken;
//        else if (data.repositoryType == "bitbucket")
//            var addRepositoryUrl = BASE_URL+"/secure/admin/AddBitbucketRepository!default.jspa";
//        AJS.$.ajax({
//            type: "post",
//            url: addRepositoryUrl,
//            data: {},
//            success:function(response) {
//                var addRepositoryContent = AJS.$('#addRepositoryContent');
//                addRepositoryContent.html(response);
//        },
//        error: function(response) {
//            alert("failed");
//        }});

    }).error(function(a){
        AJS.$("#aui-message-bar").empty();
    	AJS.messages.error({
    	    title: "Error!",
    	    body: "The repository url ["+AJS.$("#url").val()+"] is incorrect or the repository is not responding."
    	});
    });
    return false;
}

function showAddRepoDetails(show)
{
    if (show)
    {
    	AJS.$('#linkRepositoryButton').fadeOut(function(){
    		AJS.$('#addRepositoryDetails').slideDown();
    	});
    } else {
        AJS.$('#addRepositoryDetails').slideUp(function(){
        	AJS.$('#linkRepositoryButton').fadeIn();
        	AJS.$("#bbCredentials").html("");
        });
    }

}

AJS.$(document).ready(function(){
	if(typeof init_repositories == 'function')
	{
		init_repositories();
	}

//	jQuery('#footer').addClass('footer');

})

<|endoftext|>


<commit_before>'use strict';

(function (window, angular, undefined) {
	angular.module('angularYoutubePreview', []).directive('youtubePreview', [function () {
		return {
			restrict: 'E',
			replace: true,
			template: '

',
			scope: {
				v: '@',
				frameKey: '@',
				playIcon: '@',
				width: '@',
				height: '@'
			},
			link: function (scope, element, attrs) {
				var preview, playImage = angular.element('');

				if ( ! scope.frameKey) {
					scope.frameKey = 0;
				}

				preview = '//img.youtube.com/vi/' + scope.v + '/' + scope.frameKey + '.jpg'

				element.css({
					width: scope.width + 'px',
					height: scope.height + 'px',
					position: 'relative',
					background: 'url(' + preview + ') no-repeat center',
					backgroundSize: 'cover',
					cursor: 'pointer'
				});

				if (scope.playIcon) {
					playImage.attr({
						alt: 'play',
						src: scope.playIcon
					});

					playImage.css({
						position: 'absolute',
						top: '50%',
						left: '50%',
						transform: 'translate(-50%, -50%)',
					});

					element.append(playImage);
				}

				element.bind('click', function () {
					var iframe = angular.element('').attr({
						width: '100%',
						height: '100%',
						frameborder: 0,
						allowfullscreen: true,
						src: "//www.youtube.com/embed/" + scope.v + "?rel=0&autoplay=1"
					});

					element.empty();
					element.append(iframe);
				});
			}
		}
	}]);
})(window, angular);
<commit_msg>added title attr to playImage<commit_after>'use strict';

(function (window, angular, undefined) {
	angular.module('angularYoutubePreview', []).directive('youtubePreview', [function () {
		return {
			restrict: 'E',
			replace: true,
			template: '

',
			scope: {
				v: '@',
				frameKey: '@',
				playIcon: '@',
				width: '@',
				height: '@'
			},
			link: function (scope, element, attrs) {
				var preview, playImage = angular.element('');

				if ( ! scope.frameKey) {
					scope.frameKey = 0;
				}

				preview = '//img.youtube.com/vi/' + scope.v + '/' + scope.frameKey + '.jpg'

				element.css({
					width: scope.width + 'px',
					height: scope.height + 'px',
					position: 'relative',
					background: 'url(' + preview + ') no-repeat center',
					backgroundSize: 'cover',
					cursor: 'pointer'
				});

				if (scope.playIcon) {
					playImage.attr({
						alt: 'play',
						title: 'Play',
						src: scope.playIcon
					});

					playImage.css({
						position: 'absolute',
						top: '50%',
						left: '50%',
						transform: 'translate(-50%, -50%)',
					});

					element.append(playImage);
				}

				element.bind('click', function () {
					var iframe = angular.element('').attr({
						width: '100%',
						height: '100%',
						frameborder: 0,
						allowfullscreen: true,
						src: "//www.youtube.com/embed/" + scope.v + "?rel=0&autoplay=1"
					});

					element.empty();
					element.append(iframe);
				});
			}
		}
	}]);
})(window, angular);
<|endoftext|>


<commit_before>(function () {
  'use strict';

  const request = require('request');
  const marked = require('marked');
  const commander = require('commander');
  const cheerio = require('cheerio');
  const path = require('path');
  const fs = require('fs');

  var numCompletionsWritten = 0;
  var lodash = {};

  var writeCompletions = function (filename, contents) {
    filename = filename.toLowerCase();
    var destPath = path.join(__dirname, '/completions/', filename + '.sublime-completions');

    fs.writeFile(destPath, JSON.stringify(contents, null, 4), function (err) {
      if (err) {
        return console.log(err);
      }

      console.log('Completions saved to "' + destPath + '"');
    });

    numCompletionsWritten += 1;
  };

  var writeReadme = function () {
    var md = '### [lodash](' + lodash.href + ') ' + lodash.version + ' completions for Sublime Text 3\n\n![usage](example.gif)\n';
    var mdPath = path.join(__dirname, 'README.md');

    fs.writeFile(mdPath, md, function (err) {
      if (err) {
        return console.log(err);
      }

      console.log('README.md updated to ' + lodash.version);
    });
  };

  var getDocumentationUrl = function (version) {
    return 'https://raw.githubusercontent.com/lodash/lodash/#/doc/README.md'.replace('#', version);
  };

  var getDocumentation = function (callback) {
    var url = getDocumentationUrl(commander.tag);

    request(url, function (error, response, body) {
      if (!error && response.statusCode === 200) {
        callback(marked(body));
      } else if (response.statusCode === 404) {
        return console.log('Version "' + commander.tag + '" could not be found.');
      }
    });
  };

  var parseDocumentation = function (html) {
    var $ = cheerio.load(html);
    var numCompletions = 0;
    lodash.version = $('span').first().text().replace('v', '').trim();
    lodash.href = $('a').first().attr('href').trim();

    $('h2').each(function () {
      if ($(this).next().is('h3')) {
        numCompletions += 1;

        var completionsData = {
          scope: 'source.js',
          completions: [],
        };

        var group = $(this).text().replace(/(“|” Methods)/gi, '').trim();

        var codeSnippets = $(this).nextUntil('h2', 'h3');
        codeSnippets.each(function () {
          var trigger = $(this).text().trim();
          var hasParams = trigger.match(/(?=\(([^)]+)\))/g);
          var contents = trigger;

          if (trigger[0] === '_') {
            var len = trigger.length;
            trigger = commander.namespace + trigger.substring(1, len);
          }

          if (hasParams) {

            $(this).nextUntil('ol').next().children('li').each(function (i) {
              i = i + 1;
              var code = $(this).children('code').first().text();
              contents = contents.replace(code, '${' + i + ':' + code + '}');
            });

          }

          var completion = {
            trigger: trigger + '\t _ ' + group,
            contents: contents + '$0',
          };

          completionsData.completions.push(completion);
        });

        writeCompletions(group, completionsData);
      }
    });

    if (numCompletions === numCompletionsWritten) {
      writeReadme();
    }
  };

  commander
    .version('1.0.0')
    .usage('Generates completions for lodash using "' + getDocumentationUrl('master') + '"')
    .option('-t --tag [tag]', 'lodash version to fetch', 'master')
    .option('-n --namespace [namespace]', 'namespace to use in place of _', 'ld')
    .parse(process.argv);

  getDocumentation(parseDocumentation);
}());
<commit_msg>Fixed linked to example image in readme<commit_after>(function () {
  'use strict';

  const request = require('request');
  const marked = require('marked');
  const commander = require('commander');
  const cheerio = require('cheerio');
  const path = require('path');
  const fs = require('fs');

  var numCompletionsWritten = 0;
  var lodash = {};

  var writeCompletions = function (filename, contents) {
    filename = filename.toLowerCase();
    var destPath = path.join(__dirname, '/completions/', filename + '.sublime-completions');

    fs.writeFile(destPath, JSON.stringify(contents, null, 4), function (err) {
      if (err) {
        return console.log(err);
      }

      console.log('Completions saved to "' + destPath + '"');
    });

    numCompletionsWritten += 1;
  };

  var writeReadme = function () {
    var md = '### [lodash](' + lodash.href + ') ' + lodash.version + ' completions for Sublime Text 3\n\n![usage](http://i.imgur.com/NBUV56j.gif)\n';
    var mdPath = path.join(__dirname, 'README.md');

    fs.writeFile(mdPath, md, function (err) {
      if (err) {
        return console.log(err);
      }

      console.log('README.md updated to ' + lodash.version);
    });
  };

  var getDocumentationUrl = function (version) {
    return 'https://raw.githubusercontent.com/lodash/lodash/#/doc/README.md'.replace('#', version);
  };

  var getDocumentation = function (callback) {
    var url = getDocumentationUrl(commander.tag);

    request(url, function (error, response, body) {
      if (!error && response.statusCode === 200) {
        callback(marked(body));
      } else if (response.statusCode === 404) {
        return console.log('Version "' + commander.tag + '" could not be found.');
      }
    });
  };

  var parseDocumentation = function (html) {
    var $ = cheerio.load(html);
    var numCompletions = 0;
    lodash.version = $('span').first().text().replace('v', '').trim();
    lodash.href = $('a').first().attr('href').trim();

    $('h2').each(function () {
      if ($(this).next().is('h3')) {
        numCompletions += 1;

        var completionsData = {
          scope: 'source.js',
          completions: [],
        };

        var group = $(this).text().replace(/(“|” Methods)/gi, '').trim();

        var codeSnippets = $(this).nextUntil('h2', 'h3');
        codeSnippets.each(function () {
          var trigger = $(this).text().trim();
          var hasParams = trigger.match(/(?=\(([^)]+)\))/g);
          var contents = trigger;

          if (trigger[0] === '_') {
            var len = trigger.length;
            trigger = commander.namespace + trigger.substring(1, len);
          }

          if (hasParams) {

            $(this).nextUntil('ol').next().children('li').each(function (i) {
              i = i + 1;
              var code = $(this).children('code').first().text();
              contents = contents.replace(code, '${' + i + ':' + code + '}');
            });

          }

          var completion = {
            trigger: trigger + '\t _ ' + group,
            contents: contents + '$0',
          };

          completionsData.completions.push(completion);
        });

        writeCompletions(group, completionsData);
      }
    });

    if (numCompletions === numCompletionsWritten) {
      writeReadme();
    }
  };

  commander
    .version('1.0.0')
    .usage('Generates completions for lodash using "' + getDocumentationUrl('master') + '"')
    .option('-t --tag [tag]', 'lodash version to fetch', 'master')
    .option('-n --namespace [namespace]', 'namespace to use in place of _', 'ld')
    .parse(process.argv);

  getDocumentation(parseDocumentation);
}());
<|endoftext|>


<commit_before>var _ = require('underscore')
  , fs = require('fs')
  , path = require('path')
  , mkdirp = require('mkdirp')
  , DepTree = require('./deptree')
;

var rLeadingSlash = /^\//
  , rJSExt = /\.js$/
;

var _utils = {
    readFileSyncRel: function(pathRelativeToThisFile) {
        return fs.readFileSync(path.resolve(__dirname, pathRelativeToThisFile), { encoding: 'utf8' });
    },

    indent: function(str) {
        var indent = '    ';
        return indent + str.split('\n').join('\n' + indent);
    },

    normalizePath: function(path) {
        var norm = path;
        norm = norm.replace(rLeadingSlash, '');
        norm = rJSExt.test(norm) ? norm : (norm + '.js');
        return norm;
    },

    resolveModule: function(modulesDir, moduleName) {
        var modulesDirNorm = modulesDir;
        modulesDirNorm = (modulesDirNorm[modulesDirNorm.length - 1] === '/') ? modulesDirNorm : (modulesDirNorm + '/');
        return _utils.normalizePath(modulesDirNorm + moduleName);
    },

    resolveModules: function(modulesDir, moduleNames) {
        var utils = this;
        return moduleNames.map(function(moduleName) {
            return utils.resolveModule(modulesDir, moduleName);
        });
    },

    resolveAliases: function(aliases) {
        return _
            .values(aliases)
            .filter(function(value) {
                return rLeadingSlash.test(value) || rJSExt.test(value);
            })
            .map(function(pathDirty) {
                var pathRel = pathDirty;
                pathRel = pathRel.replace(rLeadingSlash, '');
                pathRel = rJSExt.test(pathRel) ? pathRel : (pathRel + '.js');
                return pathRel;
            })
        ;
    },

    validateConfig: function(config, propNames) {
        propNames.forEach(function(propName) {
            if (!config[propName]) {
                throw new Error('Required config property "' + propName + '" not found');
            }
        });
    },

    replace: function(source, find, replacement) {
        var regex = new RegExp('[ \\t]*(?:/\\*!?)?__' + find + '__(?:!?\\*/)?[ \\t]*', 'g');
        return source.replace(regex, replacement);
    }
};

var Compiler = function(config) {
    _utils.validateConfig(config, [ 'src_root', 'main', 'dest' ]);

    this.config = config;

    config.user_modules = config.user_modules || [];
    config.aliases = config.aliases || {};
    config.globals = config.globals || {};

    var srcRootAbs = config.src_root,
        main = _utils.normalizePath(config.main),
        userModules = _utils.resolveModules(config.modules_dir, config.user_modules),
        aliasModules = _utils.resolveAliases(config.aliases),
        srcFilePathsRel = [].concat(main, userModules, aliasModules);

    srcFilePathsRel = _.uniq(srcFilePathsRel);

    this.depTree = new DepTree(srcRootAbs, srcFilePathsRel);
};

Compiler.prototype = {

    compile: function() {
        this.depTree.scan(this._onScanComplete.bind(this));
    },

    _addGlobals: function(runtime) {
        var names = _.keys(this.config.globals),
            values = _.values(this.config.globals);

        if (this.config.safe_undefined === true) {
            names.push('undefined');
        }

        var retVal = runtime
          , intro = ''
          , outro = ''
        ;

        if (names.length) {
            retVal = _utils.indent(retVal);
            intro = '(function(' + names.join(', ') + ') {';
            outro = '}(' + values.join(', ') + '));';
        }

        retVal = _utils.replace(retVal, 'GLOBAL_NAMES',  intro);
        retVal = _utils.replace(retVal, 'GLOBAL_VALUES', outro);

        return retVal;
    },

    _onScanComplete: function() {
        var moduleTpl = _utils.readFileSyncRel('../runtime/module-definition.tpl.js')
          , runtimeTpl = _utils.readFileSyncRel('../runtime/runtime.js')
        ;
        var moduleDefs = this.depTree.dependencies.map(
            /**
             * @param {File} file
             * @returns {String}
             */
            function(file) {
                return moduleTpl
                    .replace(/__PATH__/g, file.pathCanonical)
                    .replace(/__SOURCE__/g, _utils.indent(file.canonicalSource.trim()))
                    .trim()
                ;
            }
        );

        var config = {
            main: '/' + _utils.normalizePath(this.config.main),
            aliases: this.config.aliases
        };

        var configArg = JSON.stringify(config, null, 4);
        var moduleDefsArg = '{\n' + _utils.indent(moduleDefs.join(',\n')) + '\n}';

        var runtime = runtimeTpl;
        runtime = _utils.replace(runtime, 'CONFIG', _utils.indent(configArg));
        runtime = _utils.replace(runtime, 'MODULE_DEFINITIONS', _utils.indent(moduleDefsArg));
        runtime = this._addGlobals(runtime);

        mkdirp.sync(path.dirname(this.config.dest));
        fs.writeFileSync(this.config.dest, runtime);
    }

};

module.exports = Compiler;
<commit_msg>Strip single-line comments from runtime template<commit_after>var _ = require('underscore')
  , fs = require('fs')
  , path = require('path')
  , mkdirp = require('mkdirp')
  , DepTree = require('./deptree')
;

var rLeadingSlash = /^\//
  , rJSExt = /\.js$/
  , rLineComment  = new RegExp('[ \\t]*//.*', 'g')
;

var _utils = {
    readFileSyncRel: function(pathRelativeToThisFile) {
        return fs.readFileSync(path.resolve(__dirname, pathRelativeToThisFile), { encoding: 'utf8' });
    },

    indent: function(str) {
        var indent = '    ';
        return indent + str.split('\n').join('\n' + indent);
    },

    normalizePath: function(path) {
        var norm = path;
        norm = norm.replace(rLeadingSlash, '');
        norm = rJSExt.test(norm) ? norm : (norm + '.js');
        return norm;
    },

    resolveModule: function(modulesDir, moduleName) {
        var modulesDirNorm = modulesDir;
        modulesDirNorm = (modulesDirNorm[modulesDirNorm.length - 1] === '/') ? modulesDirNorm : (modulesDirNorm + '/');
        return _utils.normalizePath(modulesDirNorm + moduleName);
    },

    resolveModules: function(modulesDir, moduleNames) {
        var utils = this;
        return moduleNames.map(function(moduleName) {
            return utils.resolveModule(modulesDir, moduleName);
        });
    },

    resolveAliases: function(aliases) {
        return _
            .values(aliases)
            .filter(function(value) {
                return rLeadingSlash.test(value) || rJSExt.test(value);
            })
            .map(function(pathDirty) {
                var pathRel = pathDirty;
                pathRel = pathRel.replace(rLeadingSlash, '');
                pathRel = rJSExt.test(pathRel) ? pathRel : (pathRel + '.js');
                return pathRel;
            })
        ;
    },

    validateConfig: function(config, propNames) {
        propNames.forEach(function(propName) {
            if (!config[propName]) {
                throw new Error('Required config property "' + propName + '" not found');
            }
        });
    },

    replace: function(source, find, replacement) {
        var regex = new RegExp('[ \\t]*(?:/\\*!?)?__' + find + '__(?:!?\\*/)?[ \\t]*', 'g');
        return source.replace(regex, replacement);
    },

    stripLineComments: function(source) {
        return source.replace(rLineComment, '');
    }
};

var Compiler = function(config) {
    _utils.validateConfig(config, [ 'src_root', 'main', 'dest' ]);

    this.config = config;

    config.user_modules = config.user_modules || [];
    config.aliases = config.aliases || {};
    config.globals = config.globals || {};

    var srcRootAbs = config.src_root,
        main = _utils.normalizePath(config.main),
        userModules = _utils.resolveModules(config.modules_dir, config.user_modules),
        aliasModules = _utils.resolveAliases(config.aliases),
        srcFilePathsRel = [].concat(main, userModules, aliasModules);

    srcFilePathsRel = _.uniq(srcFilePathsRel);

    this.depTree = new DepTree(srcRootAbs, srcFilePathsRel);
};

Compiler.prototype = {

    compile: function() {
        this.depTree.scan(this._onScanComplete.bind(this));
    },

    _addGlobals: function(runtime) {
        var names = _.keys(this.config.globals),
            values = _.values(this.config.globals);

        if (this.config.safe_undefined === true) {
            names.push('undefined');
        }

        var retVal = runtime
          , intro = ''
          , outro = ''
        ;

        if (names.length) {
            retVal = _utils.indent(retVal);
            intro = '(function(' + names.join(', ') + ') {';
            outro = '}(' + values.join(', ') + '));';
        }

        retVal = _utils.replace(retVal, 'GLOBAL_NAMES',  intro);
        retVal = _utils.replace(retVal, 'GLOBAL_VALUES', outro);

        return retVal;
    },

    _onScanComplete: function() {
        var moduleTpl = _utils.readFileSyncRel('../runtime/module-definition.tpl.js')
          , runtimeTpl = _utils.readFileSyncRel('../runtime/runtime.js')
        ;
        var moduleDefs = this.depTree.dependencies.map(
            /**
             * @param {File} file
             * @returns {String}
             */
            function(file) {
                return moduleTpl
                    .replace(/__PATH__/g, file.pathCanonical)
                    .replace(/__SOURCE__/g, _utils.indent(file.canonicalSource.trim()))
                    .trim()
                ;
            }
        );

        var config = {
            main: '/' + _utils.normalizePath(this.config.main),
            aliases: this.config.aliases
        };

        var configArg = JSON.stringify(config, null, 4);
        var moduleDefsArg = '{\n' + _utils.indent(moduleDefs.join(',\n')) + '\n}';

        var runtime = runtimeTpl;
        runtime = _utils.stripLineComments(runtime);
        runtime = _utils.replace(runtime, 'CONFIG', _utils.indent(configArg));
        runtime = _utils.replace(runtime, 'MODULE_DEFINITIONS', _utils.indent(moduleDefsArg));
        runtime = this._addGlobals(runtime);

        mkdirp.sync(path.dirname(this.config.dest));
        fs.writeFileSync(this.config.dest, runtime);
    }

};

module.exports = Compiler;
<|endoftext|>


<commit_before>gfw.ui.model.CountryHeaderStatus = cdb.core.Model.extend({});

gfw.ui.view.CountryHeader = cdb.core.View.extend({

  el: $('.country-header'),

  events: {
    'change #areaSelector': '_onSelectArea',
    'click .selector-remove': '_navigateCountry'
  },

  initialize: function(options) {
    _.extend(this, options);
    var self = this;

    // Cache
    this.$areaSelector = this.$('#areaSelector');
    this.$selectorRemove =  this.$('.selector-remove');
    this.$map = this.$('.map');

    var Router = Backbone.Router.extend({
      routes: {
        'country/:id': 'loadCountry',
        'country/:id/': 'loadCountry',
        'country/:id/:areaId': 'loadArea',
        'country/:id/:areaId/': 'loadArea'
      },

      initialize: function() {
        self._drawLossAndGain();
      },

      loadArea: function(countryId, areaId) {
        var area = self.country.get('areas').where({ id_1: Number(areaId) })[0];
        self.area = area;
        if (!self.map) {
          self._setAreaSelector();
          self._initMap(function() {
            self._displayArea(area);
          });
        } else {
          self._displayArea(area);
        }
      },

      loadCountry: function(countryId) {
        if (!self.map) {
          self._setAreaSelector();
          self._initMap(function() {
            self._displayCountry();
          });
        } else {
          self._displayCountry();
        }
      }

    });

    this.country.fetch({
      success: function() {
        self.router = new Router();
        Backbone.history.start({pushState: true});
      }
    });
  },

  _setAreaSelector: function() {
    var self = this;

    _.each(this.country.get('areas').models, function(area) {
      if (self.area == area) {
        self.$areaSelector.append('<option value="' + area.get('name_1') + '" selected>' + area.get('name_1') + '</option>')
      } else {
        self.$areaSelector.append('<option value="' + area.get('name_1') + '">' + area.get('name_1') + '</option>')
      }
    });
  },

  _onSelectArea: function() {
    var self = this,
        areaName = this.$areaSelector.val(),
        area = this.country.get('areas').where({ name_1: areaName })[0];

    if (area) {
      this.router.navigate('country/' + this.country.get('iso') + '/' + String(area.get('id_1')), {trigger: true});
    } else {
      this._navigateCountry();
    }
  },

  _navigateCountry: function() {
    this.router.navigate('country/' + this.country.get('iso'), {trigger: true});
  },

  _initMap: function(callback) {
    var self = this,
        sql = new cartodb.SQL({ user: 'wri-01' });

    sql.execute("SELECT bounds FROM country_mask WHERE code = '" + this.country.get('iso')  + "'")
      .done(function(data) {
        var geojson = L.geoJson(JSON.parse(data.rows[0].bounds)),
            bounds = geojson.getBounds();

        self.country.set('bounds', bounds);
        self._renderMap(callback);
      });
  },

  _filterCanvasImage: function(imageData, w, h) {
    var components = 4,
        pixelPos;

    for(var i = 0 ; i < w; ++i) {
      for(var j = 0; j < h; ++j) {
        var pixelPos = (j * w + i) * components,
            intensity = imageData[pixelPos + 3];

        imageData[pixelPos] = 0;
        imageData[pixelPos + 1] = intensity * 0.7;
        imageData[pixelPos + 2] = 250;
        imageData[pixelPos+ 3] = intensity * 0.7;
      }
    }
  },

  _drawImageCanvas: function(canvas) {
    var ctx = canvas.getContext('2d'),
        coord = canvas.coord;

    if (canvas.coord) {
      var zsteps = coord.z - 12;

      if (zsteps > 0) {
        ctx['imageSmoothingEnabled'] = false;
        ctx['mozImageSmoothingEnabled'] = false;
        ctx['webkitImageSmoothingEnabled'] = false;

        var srcX = 256 / Math.pow(2, zsteps) * (coord.x % Math.pow(2, zsteps)),
            srcY = 256 / Math.pow(2, zsteps) * (coord.y % Math.pow(2, zsteps)),
            srcW = 256 / Math.pow(2, zsteps),
            srcH = 256 / Math.pow(2, zsteps);
        ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256);

      } else {
        try {
          ctx.drawImage(canvas.image, 0, 0);
        } catch(err) { }
      }

      var I = ctx.getImageData(0, 0, canvas.width, canvas.height);
      this._filterCanvasImage(I.data, canvas.width, canvas.height);
      ctx.putImageData(I, 0, 0);
    }
  },

  _renderMap: function(callback) {
    var self = this;

    // Map & layers
    this.cartodbLayer = {};
    this.forestLayer = {};
    this.map = {};

    this.map = new L.Map('countryMap', {
      center: [0, 0],
      maxBounds: this.country.get('bounds'),
      zoom: 3,
      zoomControl: false,
      dragging: false,
      touchZoom: false,
      doubleClickZoom: false,
      scrollWheelZoom: false,
      attributionControl: false,
      zoomAnimation: false,
      fadeAnimation: false,
    });

    this.forestLayer = L.tileLayer.canvas().addTo(this.map);

    this.forestLayer.drawTile = function(canvas, tilePoint, zoom) {
      var xhr = new XMLHttpRequest(),
          ctx = canvas.getContext('2d');

      var x = tilePoint.x,
          y = tilePoint.y,
          z = zoom;

      // floor = round a number downward to its nearest integer
      // pow = return the value of the number 4 to be the power of 3 (4*4*4):
      if (zoom > 12) {
        x = Math.floor(x / (Math.pow(2, zoom - 12)));
        y = Math.floor(y / (Math.pow(2, zoom - 12)));
        z = 12;
      } else {
        y = (y > Math.pow(2, z) ? y % Math.pow(2,z) : y);
        if (x >= Math.pow(2, z)) {
          x = x % Math.pow(2, z);
        } else if (x < 0) {
          x = Math.pow(2,z) - Math.abs(x);
        }
      }

      var url = 'http://earthengine.google.org/static/hansen_2013/tree_alpha/' + z + '/' + x + '/' + y + '.png';

      xhr.onload = function () {
        var url = URL.createObjectURL(this.response),
            image = new Image();

        image.onload = function () {
          image.crossOrigin = '';

          canvas.image = image;
          canvas.coord = {x: tilePoint.x, y: tilePoint.y};
          canvas.coord.z = zoom;

          self._drawImageCanvas(canvas);

          URL.revokeObjectURL(url);
        };

        image.src = url;
      };

      xhr.open('GET', url, true);
      xhr.responseType = 'blob';
      xhr.send();
    }

    // Set country layer
    cartodb.createLayer(this.map, {
      user_name: 'wri-01',
      type: 'cartodb',
      cartodb_logo: false,
      sublayers: [{
        sql: "SELECT * FROM country_mask"
      }, {
        sql: "SELECT * FROM gadm_1_all"
      }]
    })
    .addTo(this.map)
    .done(function(layer) {
      self.cartodbLayer = layer;

      self.cartodbLayer.on('loading' , function() {
        self.$map.removeClass('loaded');
        // self.forestLayer.setOpacity(0);
      });

      self.cartodbLayer.on('load' , function() {
        setTimeout(function() {
          self.$map.addClass('loaded');
          // self.forestLayer.setOpacity(1);
        }, 200);
      });

      callback();
    });
  },

  _displayCountry: function() {
    this.$selectorRemove.hide();
    this.$areaSelector.val('');
    this.map.fitBounds(this.country.get('bounds'));
    this.cartodbLayer.getSubLayer(1).hide();
    this.cartodbLayer.getSubLayer(0)
      .set({
        cartocss: "\
          #country_mask {\
            polygon-fill: #373442;\
            polygon-opacity: 1;\
            line-color: #373442;\
            line-width: 1;\
            line-opacity: 1;\
          }\
          #country_mask[code='" + this.country.get('iso') + "'] {\
            polygon-opacity: 0;\
            line-color: #73707D;\
            line-width: 1;\
            line-opacity: 1;\
          }"
      });
  },

  _displayArea: function(area) {
    this.$selectorRemove.show();
    this.map.fitBounds(area.get('bounds'), {reset: true});
    this.cartodbLayer.getSubLayer(1)
      .set({
        cartocss: "\
          #gadm_1_all {\
            polygon-fill: #373442;\
            polygon-opacity: 1;\
            line-color: #373442;\
            line-width: 1;\
            line-opacity: 1;\
            [cartodb_id=" + area.get('cartodb_id') + "]{\
              polygon-opacity: 0;\
            }\
            ::active[cartodb_id=" + area.get('cartodb_id') + "] {\
              polygon-opacity: 0;\
              line-color: #73707D;\
              line-width: 1;\
              line-opacity: 1;\
            }\
          }"
      })
      .show();

    this.cartodbLayer.getSubLayer(0)
      .set({
        cartocss: "\
          #country_mask {\
            polygon-fill: #373442;\
            polygon-opacity: 1;\
            line-color: #373442;\
            line-width: 1;\
            line-opacity: 1;\
          }\
          #country_mask[code='" + this.country.get('iso') + "'] {\
            polygon-opacity: 0;\
            line-color: #373442;\
            line-width: 1;\
            line-opacity: 1;\
          }"
      });
  },

  _drawLossAndGain: function() {
    var sql = "SELECT year, loss_gt_0 loss FROM umd WHERE iso='" + this.country.get('iso') + "' ORDER BY year ASC",
        that = this;

    var $graph = this.$('.loss-gain-graph'),
        $comingSoon = $graph.find('.coming-soon'),
        $amount = $graph.find('.graph-amount'),
        $date = $graph.find('.graph-date');

    var width     = 200,
        height    = 90,
        h         = 90, // maxHeight
        radius    = width / 2;

    var graph = d3.select('.loss-gain-graph .graph')
      .append('svg:svg')
      .attr('width', width)
      .attr('height', height);

    d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + sql, function(json) {
      if (json) {
        $graph.removeClass('ghost');
        var data = json.rows;
      } else {
        $comingSoon.show();
        return;
      }

      var data_ = [];

      _.each(data, function(val, key) {
        if (val.year >= 2001) {
          data_.push({
            'year': val.year,
            'value': val.loss//eval('val.' + options.dataset)
          });
        }
      });

      $amount.html('' + formatNumber(parseInt(data_[data_.length - 1].value, 10)) + '');
      $date.html('Hectares lost in ' + data_[data_.length - 1].year);

      var marginLeft = 5,
          marginTop = 0;

      var y_scale = d3.scale.linear()
        .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })])
        .range([height, marginTop * 2]);

      var barWidth = 16;

      var bar = graph.selectAll('g')
        .data(data_)
        .enter()
        .append('g')
        .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ',' + -marginTop + ')'; });

      bar.append('svg:rect')
        .attr('class', 'bar')
        .attr('y', function(d) { return y_scale(d.value); })
        .attr('height', function(d) { return height - y_scale(d.value); })
        .attr('width', barWidth - 2)
        .style('fill', function(d, i) {
          if (i === 11) {
            return '#9FBA2B';
          } else {
            return '#524F5C';
          }
        })
        .on('mouseover', function(d) {
          d3.selectAll('.bar').style('fill', '#524F5C');
          d3.select(this).style('fill', '#9FBA2B');

          $amount.html('' + formatNumber(parseInt(d.value, 10)) + '');
          $date.html('Hectares lost in ' + d.year);
        });

      // Draw gain line
      var sql = 'SELECT y2001_y2012 as gain, (SELECT SUM(';

      for(var y = 2001; y < 2012; y++) {
        sql += 'y'+y+' + ';
      }

      sql += ['y2012) FROM countries_loss',
              "WHERE iso = '" + that.country.get('iso') + "') as loss",
              'FROM countries_gain',
              "WHERE iso = '" + that.country.get('iso') + "'"].join(' ');

      d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + encodeURIComponent(sql), function(json) {
        var gainAverage = json.rows[0].gain / 12;

        var tooltip = d3.select('.loss-gain-graph')
          .append('div')
          .attr('class', 'gain-tooltip')
          .style('visibility', 'hidden')
          .text(formatNumber(parseInt(gainAverage), 10));

        tooltip
          .append('span')
          .text('Average Ha of gain/year');

        var gainLine = graph.append('svg:rect')
          .attr('class', 'gain-line')
          .attr('x', 0)
          .attr('y', Math.abs(y_scale(gainAverage)))
          .attr('height', 1)
          .attr('width', width)
          .style('stroke', 'transparent')
          .style("stroke-width", "5")
          .style('fill', '#ccc');

        gainLine
          .on('mousemove', function() {
            d3.select('.gain-line')
              .style('height', 2)
              .style('fill', 'white');

            tooltip
              .style('visibility', 'visible')
              .style("top", Math.abs(y_scale(gainAverage)) + "px")
              .style("left", (d3.mouse(this)[0] - 58) + "px");
          })
          .on('mouseout', function() {
            d3.select('.gain-line')
              .style('height', 1)
              .style('fill', '#ccc');

            tooltip.style('visibility', 'hidden');
          })
      });

    });
  }

});<commit_msg>canvas layer model<commit_after>gfw.ui.model.CountryHeaderStatus = cdb.core.Model.extend({});

gfw.ui.model.layersOptions = Backbone.Model.extend({

  initialize: function(options) {
    options = options || {};

    var layers = {
      'forest2000': {
        url: 'http://earthengine.google.org/static/hansen_2013/tree_alpha/%z/%x/%y.png',
        dataMaxZoom: 12,
        tileSize: [256, 256],
        _filterCanvasImage: function(imageData, w, h) {
          var components = 4,
              pixelPos;

          for(var i = 0 ; i < w; ++i) {
            for(var j = 0; j < h; ++j) {
              var pixelPos = (j * w + i) * components,
                  intensity = imageData[pixelPos + 3];

              imageData[pixelPos] = 0;
              imageData[pixelPos + 1] = intensity * 0.7;
              imageData[pixelPos + 2] = 0;
              imageData[pixelPos+ 3] = intensity * 0.7;
            }
          }
        }
      }
    };

    var self = this,
        layer = layers[options.layer];

    if (layer) {
      _.each(layer, function(row, i) {
        self.set(i, row);
      })
    }
  },

});

gfw.ui.view.leafletCanvasLayer = Backbone.View.extend({

  initialize: function(options) {
    var self = this;
    options = options || {layerName: ''};

    this.layerOptions = new gfw.ui.model.layersOptions({layer: options.layerName});
    _.extend(this, this.layerOptions.toJSON());

    this.layer = L.tileLayer.canvas();

    this.layer.drawTile = function(canvas, tilePoint, zoom) {
      var xhr = new XMLHttpRequest(),
          ctx = canvas.getContext('2d');

      var x = tilePoint.x,
          y = tilePoint.y,
          z = zoom,
          mz = self.dataMaxZoom;

      if (zoom > mz) {
        x = Math.floor(x / (Math.pow(2, zoom - mz)));
        y = Math.floor(y / (Math.pow(2, zoom - mz)));
        z = mz;
      } else {
        y = (y > Math.pow(2, z) ? y % Math.pow(2,z) : y);
        if (x >= Math.pow(2, z)) {
          x = x % Math.pow(2, z);
        } else if (x < 0) {
          x = Math.pow(2,z) - Math.abs(x);
        }
      }

      var url = self.url.replace('%z', z).replace('%x', x).replace('%y', y);

      xhr.onload = function () {
        var url = URL.createObjectURL(this.response),
            image = new Image();

        image.onload = function () {
          image.crossOrigin = '';

          canvas.image = image;
          canvas.coord = {x: tilePoint.x, y: tilePoint.y};
          canvas.coord.z = zoom;

          self._drawImageCanvas(canvas);

          URL.revokeObjectURL(url);
        };

        image.src = url;
      };

      xhr.open('GET', url, true);
      xhr.responseType = 'blob';
      xhr.send();
    }
  },

  _filterCanvasImage: function(imageData, w, h) {
    var components = 4,
        pixelPos;

    for(var i = 0 ; i < w; ++i) {
      for(var j = 0; j < h; ++j) {
        var pixelPos = (j * w + i) * components,
            intensity = imageData[pixelPos + 3];

        imageData[pixelPos] = 0;
        imageData[pixelPos + 1] = intensity * 0.7;
        imageData[pixelPos + 2] = 255;
        imageData[pixelPos+ 3] = intensity * 0.7;
      }
    }
  },

  _drawImageCanvas: function(canvas) {
    var ctx = canvas.getContext('2d'),
        coord = canvas.coord;

    if (canvas.coord) {
      var zsteps = coord.z - this.dataMaxZoom;

      if (zsteps > 0) {
        ctx['imageSmoothingEnabled'] = false;
        ctx['mozImageSmoothingEnabled'] = false;
        ctx['webkitImageSmoothingEnabled'] = false;

        var srcX = 256 / Math.pow(2, zsteps) * (coord.x % Math.pow(2, zsteps)),
            srcY = 256 / Math.pow(2, zsteps) * (coord.y % Math.pow(2, zsteps)),
            srcW = 256 / Math.pow(2, zsteps),
            srcH = 256 / Math.pow(2, zsteps);
        ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256);

      } else {
        try {
          ctx.drawImage(canvas.image, 0, 0);
        } catch(err) { }
      }

      var I = ctx.getImageData(0, 0, canvas.width, canvas.height);
      this._filterCanvasImage(I.data, canvas.width, canvas.height);
      ctx.putImageData(I, 0, 0);
    }
  },

  getLayer: function() {
    return this.layer;
  }

});

gfw.ui.view.CountryHeader = cdb.core.View.extend({

  el: $('.country-header'),

  events: {
    'change #areaSelector': '_onSelectArea',
    'click .selector-remove': '_navigateCountry'
  },

  initialize: function(options) {
    _.extend(this, options);
    var self = this;

    // Cache
    this.$areaSelector = this.$('#areaSelector');
    this.$selectorRemove =  this.$('.selector-remove');
    this.$map = this.$('.map');

    var Router = Backbone.Router.extend({
      routes: {
        'country/:id': 'loadCountry',
        'country/:id/': 'loadCountry',
        'country/:id/:areaId': 'loadArea',
        'country/:id/:areaId/': 'loadArea'
      },

      initialize: function() {
        self._drawLossAndGain();
      },

      loadArea: function(countryId, areaId) {
        var area = self.country.get('areas').where({ id_1: Number(areaId) })[0];
        self.area = area;
        if (!self.map) {
          self._setAreaSelector();
          self._initMap(function() {
            self._displayArea(area);
          });
        } else {
          self._displayArea(area);
        }
      },

      loadCountry: function(countryId) {
        if (!self.map) {
          self._setAreaSelector();
          self._initMap(function() {
            self._displayCountry();
          });
        } else {
          self._displayCountry();
        }
      }

    });

    this.country.fetch({
      success: function() {
        self.router = new Router();
        Backbone.history.start({pushState: true});
      }
    });
  },

  _setAreaSelector: function() {
    var self = this;

    _.each(this.country.get('areas').models, function(area) {
      if (self.area == area) {
        self.$areaSelector.append('<option value="' + area.get('name_1') + '" selected>' + area.get('name_1') + '</option>')
      } else {
        self.$areaSelector.append('<option value="' + area.get('name_1') + '">' + area.get('name_1') + '</option>')
      }
    });
  },

  _onSelectArea: function() {
    var self = this,
        areaName = this.$areaSelector.val(),
        area = this.country.get('areas').where({ name_1: areaName })[0];

    if (area) {
      this.router.navigate('country/' + this.country.get('iso') + '/' + String(area.get('id_1')), {trigger: true});
    } else {
      this._navigateCountry();
    }
  },

  _navigateCountry: function() {
    this.router.navigate('country/' + this.country.get('iso'), {trigger: true});
  },

  _initMap: function(callback) {
    var self = this,
        sql = new cartodb.SQL({ user: 'wri-01' });

    sql.execute("SELECT bounds FROM country_mask WHERE code = '" + this.country.get('iso')  + "'")
      .done(function(data) {
        var geojson = L.geoJson(JSON.parse(data.rows[0].bounds)),
            bounds = geojson.getBounds();

        self.country.set('bounds', bounds);
        self._renderMap(callback);
      });
  },

  _renderMap: function(callback) {
    var self = this;

    // Map & layers
    this.cartodbLayer = {};
    this.forestLayer = {};
    this.map = {};

    this.map = new L.Map('countryMap', {
      center: [0, 0],
      zoom: 3,
      zoomControl: false,
      dragging: false,
      touchZoom: false,
      doubleClickZoom: false,
      scrollWheelZoom: false,
      attributionControl: false,
      zoomAnimation: false,
      fadeAnimation: false,
    });

    this.forestLayer = new gfw.ui.view.leafletCanvasLayer({
      layerName: 'forest2000'
    }).getLayer().addTo(this.map);

    // Set country layer
    cartodb.createLayer(this.map, {
      user_name: 'wri-01',
      type: 'cartodb',
      cartodb_logo: false,
      sublayers: [{
        sql: "SELECT * FROM country_mask"
      }, {
        sql: "SELECT * FROM gadm_1_all"
      }]
    })
    .addTo(this.map)
    .done(function(layer) {
      self.cartodbLayer = layer;
      self.cartodbLayer.on('loading' , function() {
        self.$map.removeClass('loaded');
        self.forestLayer.setOpacity(0);
      });

      self.cartodbLayer.on('load' , function() {
        self.$map.addClass('loaded');
        self.forestLayer.setOpacity(1);
      });

      callback();
    });
  },

  _displayCountry: function() {
    this.$selectorRemove.hide();
    this.$areaSelector.val('');
    this.map.fitBounds(this.country.get('bounds'));
    this.cartodbLayer.getSubLayer(1).hide();
    this.cartodbLayer.getSubLayer(0)
      .set({
        cartocss: "\
          #country_mask {\
            polygon-fill: #373442;\
            polygon-opacity: 1;\
            line-color: #373442;\
            line-width: 1;\
            line-opacity: 1;\
          }\
          #country_mask[code='" + this.country.get('iso') + "'] {\
            polygon-opacity: 0;\
            line-color: #73707D;\
            line-width: 1;\
            line-opacity: 1;\
          }"
      });
  },

  _displayArea: function(area) {
    this.$selectorRemove.show();
    this.map.fitBounds(area.get('bounds'), {reset: true});
    this.cartodbLayer.getSubLayer(1)
      .set({
        cartocss: "\
          #gadm_1_all {\
            polygon-fill: #373442;\
            polygon-opacity: 1;\
            line-color: #373442;\
            line-width: 1;\
            line-opacity: 1;\
            [cartodb_id=" + area.get('cartodb_id') + "]{\
              polygon-opacity: 0;\
            }\
            ::active[cartodb_id=" + area.get('cartodb_id') + "] {\
              polygon-opacity: 0;\
              line-color: #73707D;\
              line-width: 1;\
              line-opacity: 1;\
            }\
          }"
      })
      .show();

    this.cartodbLayer.getSubLayer(0)
      .set({
        cartocss: "\
          #country_mask {\
            polygon-fill: #373442;\
            polygon-opacity: 1;\
            line-color: #373442;\
            line-width: 1;\
            line-opacity: 1;\
          }\
          #country_mask[code='" + this.country.get('iso') + "'] {\
            polygon-opacity: 0;\
            line-color: #373442;\
            line-width: 1;\
            line-opacity: 1;\
          }"
      });
  },

  _drawLossAndGain: function() {
    var sql = "SELECT year, loss_gt_0 loss FROM umd WHERE iso='" + this.country.get('iso') + "' ORDER BY year ASC",
        that = this;

    var $graph = this.$('.loss-gain-graph'),
        $comingSoon = $graph.find('.coming-soon'),
        $amount = $graph.find('.graph-amount'),
        $date = $graph.find('.graph-date');

    var width     = 200,
        height    = 90,
        h         = 90, // maxHeight
        radius    = width / 2;

    var graph = d3.select('.loss-gain-graph .graph')
      .append('svg:svg')
      .attr('width', width)
      .attr('height', height);

    d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + sql, function(json) {
      if (json) {
        $graph.removeClass('ghost');
        var data = json.rows;
      } else {
        $comingSoon.show();
        return;
      }

      var data_ = [];

      _.each(data, function(val, key) {
        if (val.year >= 2001) {
          data_.push({
            'year': val.year,
            'value': val.loss//eval('val.' + options.dataset)
          });
        }
      });

      $amount.html('' + formatNumber(parseInt(data_[data_.length - 1].value, 10)) + '');
      $date.html('Hectares lost in ' + data_[data_.length - 1].year);

      var marginLeft = 5,
          marginTop = 0;

      var y_scale = d3.scale.linear()
        .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })])
        .range([height, marginTop * 2]);

      var barWidth = 16;

      var bar = graph.selectAll('g')
        .data(data_)
        .enter()
        .append('g')
        .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ',' + -marginTop + ')'; });

      bar.append('svg:rect')
        .attr('class', 'bar')
        .attr('y', function(d) { return y_scale(d.value); })
        .attr('height', function(d) { return height - y_scale(d.value); })
        .attr('width', barWidth - 2)
        .style('fill', function(d, i) {
          if (i === 11) {
            return '#9FBA2B';
          } else {
            return '#524F5C';
          }
        })
        .on('mouseover', function(d) {
          d3.selectAll('.bar').style('fill', '#524F5C');
          d3.select(this).style('fill', '#9FBA2B');

          $amount.html('' + formatNumber(parseInt(d.value, 10)) + '');
          $date.html('Hectares lost in ' + d.year);
        });

      // Draw gain line
      var sql = 'SELECT y2001_y2012 as gain, (SELECT SUM(';

      for(var y = 2001; y < 2012; y++) {
        sql += 'y'+y+' + ';
      }

      sql += ['y2012) FROM countries_loss',
              "WHERE iso = '" + that.country.get('iso') + "') as loss",
              'FROM countries_gain',
              "WHERE iso = '" + that.country.get('iso') + "'"].join(' ');

      d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + encodeURIComponent(sql), function(json) {
        var gainAverage = json.rows[0].gain / 12;

        var tooltip = d3.select('.loss-gain-graph')
          .append('div')
          .attr('class', 'gain-tooltip')
          .style('visibility', 'hidden')
          .text(formatNumber(parseInt(gainAverage), 10));

        tooltip
          .append('span')
          .text('Average Ha of gain/year');

        var gainLine = graph.append('svg:rect')
          .attr('class', 'gain-line')
          .attr('x', 0)
          .attr('y', Math.abs(y_scale(gainAverage)))
          .attr('height', 1)
          .attr('width', width)
          .style('stroke', 'transparent')
          .style("stroke-width", "5")
          .style('fill', '#ccc');

        gainLine
          .on('mousemove', function() {
            d3.select('.gain-line')
              .style('height', 2)
              .style('fill', 'white');

            tooltip
              .style('visibility', 'visible')
              .style("top", Math.abs(y_scale(gainAverage)) + "px")
              .style("left", (d3.mouse(this)[0] - 58) + "px");
          })
          .on('mouseout', function() {
            d3.select('.gain-line')
              .style('height', 1)
              .style('fill', '#ccc');

            tooltip.style('visibility', 'hidden');
          })
      });

    });
  }

});<|endoftext|>