<commit_before>import React, { Component } from 'react'
import { connect } from 'react-redux'
import { FormattedMessage, injectIntl } from 'react-intl'
import PropTypes from 'prop-types'

import { extractHeadsignFromPattern, getColorAndNameFromRoute } from '../../util/viewer'
import Icon from '../util/icon'
import { getVehiclePositionsForRoute, findStopsForPattern } from '../../actions/api'
import { setHoveredStop, setViewedStop, setViewedRoute } from '../../actions/ui'

import {
  Container,
  RouteNameContainer,
  LogoLinkContainer,
  PatternContainer,
  StopContainer,
  Stop
} from './styled'

class RouteDetails extends Component {
  static propTypes = {
    className: PropTypes.string,
    findStopsForPattern: findStopsForPattern.type,
    operator: PropTypes.shape({
      defaultRouteColor: PropTypes.string,
      defaultRouteTextColor: PropTypes.string,
      longNameSplitter: PropTypes.string
    }),
    // There are more items in pattern and route, but none mandatory
    pattern: PropTypes.shape({ id: PropTypes.string }),
    route: PropTypes.shape({ id: PropTypes.string }),
    setHoveredStop: setHoveredStop.type,
    setViewedRoute: setViewedRoute.type
  };

  componentDidMount = () => {
    const { getVehiclePositionsForRoute, pattern, route } = this.props
    if (!route.vehicles) {
      getVehiclePositionsForRoute(route.id)
    }
    if (!pattern?.stops) { this.getStops() }
  };

  componentDidUpdate = (prevProps) => {
    if (prevProps.pattern?.id !== this.props.pattern?.id) {
      this.getStops()
    }
  };

  /**
   * Requests stop list for current pattern
   */
  getStops = () => {
    const { findStopsForPattern, pattern, route } = this.props
    if (pattern && route) {
      findStopsForPattern({ patternId: pattern.id, routeId: route.id })
    }
  };

  /**
   * If a headsign link is clicked, set that pattern in redux state so that the
   * view can adjust
   */
  _headSignButtonClicked = (e) => {
    const { target } = e
    const { value: id } = target
    const { route, setViewedRoute } = this.props
    setViewedRoute({ patternId: id, routeId: route.id })
  };

  /**
   * If a stop link is clicked, redirect to stop viewer
   */
  _stopLinkClicked = (stopId) => {
    const { setViewedStop } = this.props
    setViewedStop({ stopId })
  };

  render () {
    const { intl, operator, pattern, route, setHoveredStop, viewedRoute } = this.props
    const { agency, patterns, url } = route

    const {
      backgroundColor: routeColor
    } = getColorAndNameFromRoute(operator, route)

    const headsigns =
      patterns &&
      Object.entries(patterns)
        .map((pattern) => {
          return {
            geometryLength: pattern[1].geometry?.length,
            headsign: extractHeadsignFromPattern(pattern[1]),
            id: pattern[0]
          }
        })
        // Remove duplicate headsigns. Using a reducer means that the first pattern
        // with a specific headsign is the accepted one. TODO: is this good behavior?
        .reduce((prev, cur) => {
          const amended = prev
          const alreadyExistingIndex = prev.findIndex(
            (h) => h.headsign === cur.headsign
          )
          // If the item we're replacing has less geometry, replace it!
          if (alreadyExistingIndex >= 0) {
            // Only replace if new pattern has greater geometry
            if (
              amended[alreadyExistingIndex].geometryLength < cur.geometryLength
            ) {
              amended[alreadyExistingIndex] = cur
            }
          } else {
            amended.push(cur)
          }
          return amended
        }, [])
        .sort((a, b) => {
          // sort by number of vehicles on that pattern
          const aVehicleCount = route.vehicles?.filter(
            (vehicle) => vehicle.patternId === a.id
          ).length
          const bVehicleCount = route.vehicles?.filter(
            (vehicle) => vehicle.patternId === b.id
          ).length

          // if both have the same count, sort by pattern geometry length
          if (aVehicleCount === bVehicleCount) {
            return b.geometryLength - a.geometryLength
          }
          return bVehicleCount - aVehicleCount
        })

    // if no pattern is set, we are in the routeRow
    return (
      <Container full={pattern != null}>
        <RouteNameContainer>
          <LogoLinkContainer>
            {agency && <FormattedMessage
              id='components.RouteDetails.operatedBy'
              values={{
                agencyName: agency.name
              }}
            />}
            {url && (

                <Icon type='link' />
                <FormattedMessage id='components.RouteDetails.moreDetails' />

            )}
          </LogoLinkContainer>
        </RouteNameContainer>
        <PatternContainer>

              <FormattedMessage id='components.RouteDetails.stopsTo' />

          {headsigns &&
          <select
            id='headsign-selector'
            name='headsigns'
            onBlur={this._headSignButtonClicked}
            onChange={this._headSignButtonClicked}
            value={viewedRoute?.patternId}
          >
            {!viewedRoute?.patternId && <option>{intl.formatMessage({id: 'components.RouteDetails.selectADirection'})}</option>}
            {headsigns.map((h) => (
              <option
                key={h.id}
                value={h.id}
              >
                {h.headsign}
              </option>
            ))}
          </select>}
        </PatternContainer>
        {pattern && (
          <StopContainer
            onMouseLeave={() => setHoveredStop(null)}
          >
            {pattern?.stops?.map((stop) => (
              <Stop
                key={stop.id}
                onClick={() => this._stopLinkClicked(stop.id)}
                onFocus={() => setHoveredStop(stop.id)}
                onMouseOver={() => setHoveredStop(stop.id)}
                routeColor={routeColor.includes('ffffff') ? '#333' : routeColor}
              >
                {stop.name}
              </Stop>
            ))}
          </StopContainer>
        )}
      </Container>
    )
  }
}

// connect to redux store
const mapStateToProps = (state, ownProps) => {
  return {
    viewedRoute: state.otp.ui.viewedRoute
  }
}

const mapDispatchToProps = {
  findStopsForPattern,
  getVehiclePositionsForRoute,
  setHoveredStop,
  setViewedRoute,
  setViewedStop
}

export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(RouteDetails))
<commit_msg>refactor(route-details): avoid white screen when selecting no pattern<commit_after>import React, { Component } from 'react'
import { connect } from 'react-redux'
import { FormattedMessage, injectIntl } from 'react-intl'
import PropTypes from 'prop-types'

import { extractHeadsignFromPattern, getColorAndNameFromRoute } from '../../util/viewer'
import Icon from '../util/icon'
import { getVehiclePositionsForRoute, findStopsForPattern } from '../../actions/api'
import { setHoveredStop, setViewedStop, setViewedRoute } from '../../actions/ui'

import {
  Container,
  RouteNameContainer,
  LogoLinkContainer,
  PatternContainer,
  StopContainer,
  Stop
} from './styled'

class RouteDetails extends Component {
  static propTypes = {
    className: PropTypes.string,
    findStopsForPattern: findStopsForPattern.type,
    operator: PropTypes.shape({
      defaultRouteColor: PropTypes.string,
      defaultRouteTextColor: PropTypes.string,
      longNameSplitter: PropTypes.string
    }),
    // There are more items in pattern and route, but none mandatory
    pattern: PropTypes.shape({ id: PropTypes.string }),
    route: PropTypes.shape({ id: PropTypes.string }),
    setHoveredStop: setHoveredStop.type,
    setViewedRoute: setViewedRoute.type
  };

  componentDidMount = () => {
    const { getVehiclePositionsForRoute, pattern, route } = this.props
    if (!route.vehicles) {
      getVehiclePositionsForRoute(route.id)
    }
    if (!pattern?.stops) { this.getStops() }
  };

  componentDidUpdate = (prevProps) => {
    if (prevProps.pattern?.id !== this.props.pattern?.id) {
      this.getStops()
    }
  };

  /**
   * Requests stop list for current pattern
   */
  getStops = () => {
    const { findStopsForPattern, pattern, route } = this.props
    if (pattern && route) {
      findStopsForPattern({ patternId: pattern.id, routeId: route.id })
    }
  };

  /**
   * If a headsign link is clicked, set that pattern in redux state so that the
   * view can adjust
   */
  _headSignButtonClicked = (e) => {
    const { target } = e
    const { value: id } = target
    const { route, setViewedRoute } = this.props
    setViewedRoute({ patternId: id, routeId: route.id })
  };

  /**
   * If a stop link is clicked, redirect to stop viewer
   */
  _stopLinkClicked = (stopId) => {
    const { setViewedStop } = this.props
    setViewedStop({ stopId })
  };

  render () {
    const { intl, operator, pattern, route, setHoveredStop, viewedRoute } = this.props
    const { agency, patterns, url } = route

    const {
      backgroundColor: routeColor
    } = getColorAndNameFromRoute(operator, route)

    const headsigns =
      patterns &&
      Object.entries(patterns)
        .map((pattern) => {
          return {
            geometryLength: pattern[1].geometry?.length,
            headsign: extractHeadsignFromPattern(pattern[1]),
            id: pattern[0]
          }
        })
        // Remove duplicate headsigns. Using a reducer means that the first pattern
        // with a specific headsign is the accepted one. TODO: is this good behavior?
        .reduce((prev, cur) => {
          const amended = prev
          const alreadyExistingIndex = prev.findIndex(
            (h) => h.headsign === cur.headsign
          )
          // If the item we're replacing has less geometry, replace it!
          if (alreadyExistingIndex >= 0) {
            // Only replace if new pattern has greater geometry
            if (
              amended[alreadyExistingIndex].geometryLength < cur.geometryLength
            ) {
              amended[alreadyExistingIndex] = cur
            }
          } else {
            amended.push(cur)
          }
          return amended
        }, [])
        .sort((a, b) => {
          // sort by number of vehicles on that pattern
          const aVehicleCount = route.vehicles?.filter(
            (vehicle) => vehicle.patternId === a.id
          ).length
          const bVehicleCount = route.vehicles?.filter(
            (vehicle) => vehicle.patternId === b.id
          ).length

          // if both have the same count, sort by pattern geometry length
          if (aVehicleCount === bVehicleCount) {
            return b.geometryLength - a.geometryLength
          }
          return bVehicleCount - aVehicleCount
        })

    // if no pattern is set, we are in the routeRow
    return (
      <Container full={pattern != null}>
        <RouteNameContainer>
          <LogoLinkContainer>
            {agency && <FormattedMessage
              id='components.RouteDetails.operatedBy'
              values={{
                agencyName: agency.name
              }}
            />}
            {url && (

                <Icon type='link' />
                <FormattedMessage id='components.RouteDetails.moreDetails' />

            )}
          </LogoLinkContainer>
        </RouteNameContainer>
        <PatternContainer>

              <FormattedMessage id='components.RouteDetails.stopsTo' />

          {headsigns &&
          <select
            id='headsign-selector'
            name='headsigns'
            onBlur={this._headSignButtonClicked}
            onChange={this._headSignButtonClicked}
            value={viewedRoute?.patternId}
          >
            {!viewedRoute?.patternId && <option value=''>{intl.formatMessage({id: 'components.RouteDetails.selectADirection'})}</option>}
            {headsigns.map((h) => (
              <option
                key={h.id}
                value={h.id}
              >
                {h.headsign}
              </option>
            ))}
          </select>}
        </PatternContainer>
        {pattern && (
          <StopContainer
            onMouseLeave={() => setHoveredStop(null)}
          >
            {pattern?.stops?.map((stop) => (
              <Stop
                key={stop.id}
                onClick={() => this._stopLinkClicked(stop.id)}
                onFocus={() => setHoveredStop(stop.id)}
                onMouseOver={() => setHoveredStop(stop.id)}
                routeColor={routeColor.includes('ffffff') ? '#333' : routeColor}
              >
                {stop.name}
              </Stop>
            ))}
          </StopContainer>
        )}
      </Container>
    )
  }
}

// connect to redux store
const mapStateToProps = (state, ownProps) => {
  return {
    viewedRoute: state.otp.ui.viewedRoute
  }
}

const mapDispatchToProps = {
  findStopsForPattern,
  getVehiclePositionsForRoute,
  setHoveredStop,
  setViewedRoute,
  setViewedStop
}

export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(RouteDetails))
<|endoftext|>


<commit_before>// Include gulp
var gulp = require('gulp');

// Define main directories
var assets = 'assets/';
var destination = 'build/';

// Concatenate & Minify JS
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var stripDebug = require('gulp-strip-debug');

function scripts() {
  return gulp.src([assets + 'js/vendor/jquery.min.js',
                   assets + 'js/vendor/bootstrap.min.js',
                   assets + 'js/vendor/jquery.easing.1.3.js',
                   assets + 'js/vendor/smoothscroll.js',
                   assets + 'js/vendor/owl.carousel.min.js',
                   assets + 'js/vendor/wow.js',
                   assets + 'js/vendor/Modernizr.custom.js',
                   assets + 'js/vendor/jquery-cookie.js',
                   assets + 'js/vendor/jquery-lang.js',
                   assets + 'js/vendor/jquery.mobile.custom.js',
                   assets + 'js/vendor/jquery.countdown.min.js',
                   assets + 'js/vendor/moment.js',
                   assets + 'js/vendor/moment-timezone-with-data.js',
                   assets + 'js/opensuse-news.js',
                   assets + 'js/countdown.js',
                   assets + 'js/opensuse-theme.js'])
    .pipe(concat('main.js'))
    .pipe(rename({suffix: '.min'}))
    .pipe(uglify())
    .pipe(stripDebug())
    .pipe(gulp.dest(destination + 'js'));
};

// Preprocess CSS
var less = require('gulp-less');
var path = require('path');
var minifyCss = require('gulp-clean-css');

function compileLess() {
  return gulp.src(assets + 'css/openSUSE.less')
    .pipe(less({
      paths: [ path.join(__dirname, 'less', 'includes') ]
    }))
    .pipe(rename({suffix: '.min'}))
    .pipe(minifyCss())
    .pipe(gulp.dest(destination + 'css'));
};

function vendorCSS() {
  return gulp.src([assets + 'css/vendor/animate/animate.css',
      assets + 'css/vendor/owl-carousel/owl.carousel.css',
      assets + 'css/vendor/fontawesome/font-awesome.css'])
    .pipe(concat('vendor.css'))
    .pipe(rename({suffix: '.min'}))
    .pipe(minifyCss())
    .pipe(gulp.dest(destination + 'css'))
};

function moveFonts() {
  return gulp.src(assets + 'fonts/*')
    .pipe(gulp.dest(destination + 'fonts'))
};

// Images optimization
var imagemin = require('gulp-imagemin');
var cache = require('gulp-cache');

function imagesCompression() {
  return gulp.src(assets + 'images/**/*')
    .pipe(cache(imagemin({ optimizationLevel: 7, progressive: true, interlaced: true })))
    .pipe(gulp.dest(destination + 'images'));
};

// Watch for changes in our custom assets
function watchFiles() {
  // Watch .js files
  gulp.watch(assets + 'js/*.js', scripts);
  gulp.watch(assets + 'js/vendor/*.js', scripts);
  // Watch .less files
  gulp.watch(assets + 'css/*.less', compileLess);
  // Watch image files
  gulp.watch(assets + 'images/**/*', imagesCompression);

  gulp.watch(assets + 'fonts/*', moveFonts);
};

// Run python server on localhost:8000
var serverCommand = 'python3 -m http.server 8000';
var shell = require('gulp-shell');

gulp.task('runServer', shell.task(serverCommand))
var runServer = gulp.task('runServer');

// Export tasks
exports.scripts = scripts
exports.compileLess = compileLess
exports.imagesCompression = imagesCompression
exports.moveFonts = moveFonts
exports.watchFiles = watchFiles
exports.runServer = runServer
exports.vendorCSS = gulp.series(compileLess)
exports.default = gulp.parallel(scripts, compileLess, moveFonts, vendorCSS, imagesCompression, watchFiles, runServer)
<commit_msg>Remove trailing whitespace in gulpfile.js<commit_after>// Include gulp
var gulp = require('gulp');

// Define main directories
var assets = 'assets/';
var destination = 'build/';

// Concatenate & Minify JS
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var stripDebug = require('gulp-strip-debug');

function scripts() {
  return gulp.src([assets + 'js/vendor/jquery.min.js',
                   assets + 'js/vendor/bootstrap.min.js',
                   assets + 'js/vendor/jquery.easing.1.3.js',
                   assets + 'js/vendor/smoothscroll.js',
                   assets + 'js/vendor/owl.carousel.min.js',
                   assets + 'js/vendor/wow.js',
                   assets + 'js/vendor/Modernizr.custom.js',
                   assets + 'js/vendor/jquery-cookie.js',
                   assets + 'js/vendor/jquery-lang.js',
                   assets + 'js/vendor/jquery.mobile.custom.js',
                   assets + 'js/vendor/jquery.countdown.min.js',
                   assets + 'js/vendor/moment.js',
                   assets + 'js/vendor/moment-timezone-with-data.js',
                   assets + 'js/opensuse-news.js',
                   assets + 'js/countdown.js',
                   assets + 'js/opensuse-theme.js'])
    .pipe(concat('main.js'))
    .pipe(rename({suffix: '.min'}))
    .pipe(uglify())
    .pipe(stripDebug())
    .pipe(gulp.dest(destination + 'js'));
};

// Preprocess CSS
var less = require('gulp-less');
var path = require('path');
var minifyCss = require('gulp-clean-css');

function compileLess() {
  return gulp.src(assets + 'css/openSUSE.less')
    .pipe(less({
      paths: [ path.join(__dirname, 'less', 'includes') ]
    }))
    .pipe(rename({suffix: '.min'}))
    .pipe(minifyCss())
    .pipe(gulp.dest(destination + 'css'));
};

function vendorCSS() {
  return gulp.src([assets + 'css/vendor/animate/animate.css',
      assets + 'css/vendor/owl-carousel/owl.carousel.css',
      assets + 'css/vendor/fontawesome/font-awesome.css'])
    .pipe(concat('vendor.css'))
    .pipe(rename({suffix: '.min'}))
    .pipe(minifyCss())
    .pipe(gulp.dest(destination + 'css'))
};

function moveFonts() {
  return gulp.src(assets + 'fonts/*')
    .pipe(gulp.dest(destination + 'fonts'))
};

// Images optimization
var imagemin = require('gulp-imagemin');
var cache = require('gulp-cache');

function imagesCompression() {
  return gulp.src(assets + 'images/**/*')
    .pipe(cache(imagemin({ optimizationLevel: 7, progressive: true, interlaced: true })))
    .pipe(gulp.dest(destination + 'images'));
};

// Watch for changes in our custom assets
function watchFiles() {
  // Watch .js files
  gulp.watch(assets + 'js/*.js', scripts);
  gulp.watch(assets + 'js/vendor/*.js', scripts);
  // Watch .less files
  gulp.watch(assets + 'css/*.less', compileLess);
  // Watch image files
  gulp.watch(assets + 'images/**/*', imagesCompression);

  gulp.watch(assets + 'fonts/*', moveFonts);
};

// Run python server on localhost:8000
var serverCommand = 'python3 -m http.server 8000';
var shell = require('gulp-shell');

gulp.task('runServer', shell.task(serverCommand))
var runServer = gulp.task('runServer');

// Export tasks
exports.scripts = scripts
exports.compileLess = compileLess
exports.imagesCompression = imagesCompression
exports.moveFonts = moveFonts
exports.watchFiles = watchFiles
exports.runServer = runServer
exports.vendorCSS = gulp.series(compileLess)
exports.default = gulp.parallel(scripts, compileLess, moveFonts, vendorCSS, imagesCompression, watchFiles, runServer)
<|endoftext|>


<commit_before>import all from './util/all'
import btoa from './util/btoa'
import toForm from './util/multipart'
import Connection from './connection'
import ArrayCursor from './cursor'
import Graph from './graph'
import constructCollection, {
  DocumentCollection,
  EdgeCollection
} from './collection'

export default class Database {
  constructor (config) {
    this._connection = new Connection(config)
    this._api = this._connection.route('/_api')
    this.name = this._connection.config.databaseName
  }

  route (path, headers) {
    return this._connection.route(path, headers)
  }

  // Database manipulation

  useDatabase (databaseName) {
    if (this._connection.config.databaseName === false) {
      throw new Error('Can not change database from absolute URL')
    }
    this._connection.config.databaseName = databaseName
    this._connection._databasePath = `/_db/${databaseName}`
    this.name = databaseName
    return this
  }

  useBearerAuth (token) {
    this._connection.config.headers['authorization'] = `Bearer ${token}`
    return this
  }

  useBasicAuth (username, password) {
    this._connection.config.headers['authorization'] = `Basic ${btoa(`${username || 'root'}:${password || ''}`)}`
    return this
  }

  get (cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/database/current',
      (err, res) => err ? callback(err) : callback(null, res.body.result)
    )
    return promise
  }

  createDatabase (databaseName, users, cb) {
    if (typeof users === 'function') {
      cb = users
      users = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    this._api.post(
      '/database',
      {users, name: databaseName},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  listDatabases (cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/database',
      (err, res) => err ? callback(err) : callback(null, res.body.result)
    )
    return promise
  }

  listUserDatabases (cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/database/user',
      (err, res) => err ? callback(err) : callback(null, res.body.result)
    )
    return promise
  }

  dropDatabase (databaseName, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.delete(
      `/database/${databaseName}`,
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  // Collection manipulation

  collection (collectionName) {
    return new DocumentCollection(this._connection, collectionName)
  }

  edgeCollection (collectionName) {
    return new EdgeCollection(this._connection, collectionName)
  }

  listCollections (excludeSystem, cb) {
    if (typeof excludeSystem === 'function') {
      cb = excludeSystem
      excludeSystem = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    if (typeof excludeSystem !== 'boolean') excludeSystem = true
    const resultField = (
      this._connection.arangoMajor < 3
      ? 'collections'
      : 'result'
    )
    this._api.get(
      '/collection',
      {excludeSystem},
      (err, res) => err ? callback(err) : callback(null, res.body[resultField])
    )
    return promise
  }

  collections (excludeSystem, cb) {
    if (typeof excludeSystem === 'function') {
      cb = excludeSystem
      excludeSystem = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    this.listCollections(
      excludeSystem,
      (err, collections) => (
        err
        ? callback(err)
        : callback(null, collections.map((info) => constructCollection(this._connection, info)))
      )
    )
    return promise
  }

  truncate (excludeSystem, cb) {
    if (typeof excludeSystem === 'function') {
      cb = excludeSystem
    }
    const {promise, callback} = this._connection.promisify(cb)
    this.listCollections(
      true,
      (err, collections) => (
        err
        ? callback(err)
        : all(collections.map((data) => (cb) => this._api.put(
            `/collection/${data.name}/truncate`,
            (err, res) => err ? cb(err) : cb(null, res.body)
        )), callback)
      )
    )
    return promise
  }

  // Graph manipulation

  graph (graphName) {
    return new Graph(this._connection, graphName)
  }

  listGraphs (cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/gharial',
      (err, res) => err ? callback(err) : callback(null, res.body.graphs)
    )
    return promise
  }

  graphs (cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this.listGraphs(
      (err, graphs) => err ? callback(err) : callback(null, graphs.map((info) => this.graph(info._key)))
    )
    return promise
  }

  // Queries

  transaction (collections, action, params, lockTimeout, cb) {
    if (typeof lockTimeout === 'function') {
      cb = lockTimeout
      lockTimeout = undefined
    }
    if (typeof params === 'function') {
      cb = params
      params = undefined
    }
    if (typeof params === 'number') {
      lockTimeout = params
      params = undefined
    }
    if (typeof collections === 'string' || Array.isArray(collections)) {
      collections = {write: collections}
    }
    const {promise, callback} = this._connection.promisify(cb)
    this._api.post(
      '/transaction',
      {collections, action, params, lockTimeout},
      (err, res) => err ? callback(err) : callback(null, res.body.result)
  )
    return promise
  }

  query (query, bindVars, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = undefined
    }
    if (typeof bindVars === 'function') {
      cb = bindVars
      bindVars = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    if (query && query.query) {
      if (!opts) opts = bindVars
      bindVars = query.bindVars
      query = query.query
    }
    if (query && typeof query.toAQL === 'function') {
      query = query.toAQL()
    }
    this._api.post(
      '/cursor',
      {...opts, query, bindVars},
      (err, res) => err ? callback(err) : callback(null, new ArrayCursor(this._connection, res.body))
    )
    return promise
  }

  // Function management

  listFunctions (cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/aqlfunction',
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  createFunction (name, code, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.post(
      '/aqlfunction',
      {name, code},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  dropFunction (name, group, cb) {
    if (typeof group === 'function') {
      cb = group
      group = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    this._api.delete(
      `/aqlfunction/${name}`,
      {group: Boolean(group)},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  // Service management

  listServices (cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/foxx',
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  installService (mount, source, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = undefined
    }
    const {configuration, dependencies, ...qs} = opts || {}
    const {promise, callback} = this._connection.promisify(cb)
    toForm({
      configuration,
      dependencies,
      source
    }, (err, req) => err ? callback(err) : (
      this._api.request({
        method: 'POST',
        path: '/foxx',
        rawBody: req.body,
        qs: {...qs, mount},
        headers: req.headers
      }, (err, res) => err ? callback(err) : callback(null, res.body))
    ))
    return promise
  }

  upgradeService (mount, source, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = undefined
    }
    const {configuration, dependencies, ...qs} = opts || {}
    const {promise, callback} = this._connection.promisify(cb)
    toForm({
      configuration,
      dependencies,
      source
    }, (err, req) => err ? callback(err) : (
      this._api.request({
        method: 'PATCH',
        path: '/foxx/service',
        rawBody: req.body,
        qs: {...qs, mount},
        headers: req.headers
      }, (err, res) => err ? callback(err) : callback(null, res.body))
    ))
    return promise
  }

  replaceService (mount, source, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = undefined
    }
    const {configuration, dependencies, ...qs} = opts || {}
    const {promise, callback} = this._connection.promisify(cb)
    toForm({
      configuration,
      dependencies,
      source
    }, (err, req) => err ? callback(err) : (
      this._api.request({
        method: 'PUT',
        path: '/foxx/service',
        rawBody: req.body,
        qs: {...qs, mount},
        headers: req.headers
      }, (err, res) => err ? callback(err) : callback(null, res.body))
    ))
    return promise
  }

  uninstallService (mount, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    this._api.delete(
      '/foxx/service',
      {...opts, mount},
      (err) => err ? callback(err) : callback(null)
    )
    return promise
  }

  getService (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/foxx/service',
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  getServiceConfiguration (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/foxx/configuration',
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  updateServiceConfiguration (mount, cfg, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.patch(
      '/foxx/configuration',
      cfg,
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  replaceServiceConfiguration (mount, cfg, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.put(
      '/foxx/configuration',
      cfg,
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  getServiceDependencies (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/foxx/dependencies',
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  updateServiceDependencies (mount, cfg, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.patch(
      '/foxx/dependencies',
      cfg,
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  replaceServiceDependencies (mount, cfg, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.put(
      '/foxx/dependencies',
      cfg,
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  enableServiceDevelopmentMode (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.post(
      '/foxx/development',
      undefined,
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  disableServiceDevelopmentMode (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.delete(
      '/foxx/development',
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  listServiceScripts (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/foxx/scripts',
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  runServiceScript (mount, name, args, cb) {
    if (typeof args === 'function') {
      cb = args
      args = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    this._api.post(
      `/foxx/scripts/${name}`,
      args,
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  runServiceTests (mount, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    this._api.post(
      '/foxx/tests',
      undefined,
      {...opts, mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  getServiceReadme (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/foxx/readme',
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  getServiceDocumentation (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/foxx/swagger',
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  downloadService (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.request(
      {method: 'POST', path: '/foxx/download', qs: {mount}, expectBinary: true},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  commitLocalServiceState (replace, cb) {
    if (typeof replace === 'function') {
      cb = replace;
      replace = undefined;
    }
    const {promise, callback} = this._connection.promisify(cb)
    this._api.post(
      '/foxx/commit',
      undefined,
      {replace},
      (err, res) => err ? callback(err) : callback(null)
    )
    return promise
  }
}
<commit_msg>Remove unnecessary semicolon<commit_after>import all from './util/all'
import btoa from './util/btoa'
import toForm from './util/multipart'
import Connection from './connection'
import ArrayCursor from './cursor'
import Graph from './graph'
import constructCollection, {
  DocumentCollection,
  EdgeCollection
} from './collection'

export default class Database {
  constructor (config) {
    this._connection = new Connection(config)
    this._api = this._connection.route('/_api')
    this.name = this._connection.config.databaseName
  }

  route (path, headers) {
    return this._connection.route(path, headers)
  }

  // Database manipulation

  useDatabase (databaseName) {
    if (this._connection.config.databaseName === false) {
      throw new Error('Can not change database from absolute URL')
    }
    this._connection.config.databaseName = databaseName
    this._connection._databasePath = `/_db/${databaseName}`
    this.name = databaseName
    return this
  }

  useBearerAuth (token) {
    this._connection.config.headers['authorization'] = `Bearer ${token}`
    return this
  }

  useBasicAuth (username, password) {
    this._connection.config.headers['authorization'] = `Basic ${btoa(`${username || 'root'}:${password || ''}`)}`
    return this
  }

  get (cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/database/current',
      (err, res) => err ? callback(err) : callback(null, res.body.result)
    )
    return promise
  }

  createDatabase (databaseName, users, cb) {
    if (typeof users === 'function') {
      cb = users
      users = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    this._api.post(
      '/database',
      {users, name: databaseName},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  listDatabases (cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/database',
      (err, res) => err ? callback(err) : callback(null, res.body.result)
    )
    return promise
  }

  listUserDatabases (cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/database/user',
      (err, res) => err ? callback(err) : callback(null, res.body.result)
    )
    return promise
  }

  dropDatabase (databaseName, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.delete(
      `/database/${databaseName}`,
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  // Collection manipulation

  collection (collectionName) {
    return new DocumentCollection(this._connection, collectionName)
  }

  edgeCollection (collectionName) {
    return new EdgeCollection(this._connection, collectionName)
  }

  listCollections (excludeSystem, cb) {
    if (typeof excludeSystem === 'function') {
      cb = excludeSystem
      excludeSystem = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    if (typeof excludeSystem !== 'boolean') excludeSystem = true
    const resultField = (
      this._connection.arangoMajor < 3
      ? 'collections'
      : 'result'
    )
    this._api.get(
      '/collection',
      {excludeSystem},
      (err, res) => err ? callback(err) : callback(null, res.body[resultField])
    )
    return promise
  }

  collections (excludeSystem, cb) {
    if (typeof excludeSystem === 'function') {
      cb = excludeSystem
      excludeSystem = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    this.listCollections(
      excludeSystem,
      (err, collections) => (
        err
        ? callback(err)
        : callback(null, collections.map((info) => constructCollection(this._connection, info)))
      )
    )
    return promise
  }

  truncate (excludeSystem, cb) {
    if (typeof excludeSystem === 'function') {
      cb = excludeSystem
    }
    const {promise, callback} = this._connection.promisify(cb)
    this.listCollections(
      true,
      (err, collections) => (
        err
        ? callback(err)
        : all(collections.map((data) => (cb) => this._api.put(
            `/collection/${data.name}/truncate`,
            (err, res) => err ? cb(err) : cb(null, res.body)
        )), callback)
      )
    )
    return promise
  }

  // Graph manipulation

  graph (graphName) {
    return new Graph(this._connection, graphName)
  }

  listGraphs (cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/gharial',
      (err, res) => err ? callback(err) : callback(null, res.body.graphs)
    )
    return promise
  }

  graphs (cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this.listGraphs(
      (err, graphs) => err ? callback(err) : callback(null, graphs.map((info) => this.graph(info._key)))
    )
    return promise
  }

  // Queries

  transaction (collections, action, params, lockTimeout, cb) {
    if (typeof lockTimeout === 'function') {
      cb = lockTimeout
      lockTimeout = undefined
    }
    if (typeof params === 'function') {
      cb = params
      params = undefined
    }
    if (typeof params === 'number') {
      lockTimeout = params
      params = undefined
    }
    if (typeof collections === 'string' || Array.isArray(collections)) {
      collections = {write: collections}
    }
    const {promise, callback} = this._connection.promisify(cb)
    this._api.post(
      '/transaction',
      {collections, action, params, lockTimeout},
      (err, res) => err ? callback(err) : callback(null, res.body.result)
  )
    return promise
  }

  query (query, bindVars, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = undefined
    }
    if (typeof bindVars === 'function') {
      cb = bindVars
      bindVars = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    if (query && query.query) {
      if (!opts) opts = bindVars
      bindVars = query.bindVars
      query = query.query
    }
    if (query && typeof query.toAQL === 'function') {
      query = query.toAQL()
    }
    this._api.post(
      '/cursor',
      {...opts, query, bindVars},
      (err, res) => err ? callback(err) : callback(null, new ArrayCursor(this._connection, res.body))
    )
    return promise
  }

  // Function management

  listFunctions (cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/aqlfunction',
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  createFunction (name, code, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.post(
      '/aqlfunction',
      {name, code},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  dropFunction (name, group, cb) {
    if (typeof group === 'function') {
      cb = group
      group = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    this._api.delete(
      `/aqlfunction/${name}`,
      {group: Boolean(group)},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  // Service management

  listServices (cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/foxx',
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  installService (mount, source, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = undefined
    }
    const {configuration, dependencies, ...qs} = opts || {}
    const {promise, callback} = this._connection.promisify(cb)
    toForm({
      configuration,
      dependencies,
      source
    }, (err, req) => err ? callback(err) : (
      this._api.request({
        method: 'POST',
        path: '/foxx',
        rawBody: req.body,
        qs: {...qs, mount},
        headers: req.headers
      }, (err, res) => err ? callback(err) : callback(null, res.body))
    ))
    return promise
  }

  upgradeService (mount, source, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = undefined
    }
    const {configuration, dependencies, ...qs} = opts || {}
    const {promise, callback} = this._connection.promisify(cb)
    toForm({
      configuration,
      dependencies,
      source
    }, (err, req) => err ? callback(err) : (
      this._api.request({
        method: 'PATCH',
        path: '/foxx/service',
        rawBody: req.body,
        qs: {...qs, mount},
        headers: req.headers
      }, (err, res) => err ? callback(err) : callback(null, res.body))
    ))
    return promise
  }

  replaceService (mount, source, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = undefined
    }
    const {configuration, dependencies, ...qs} = opts || {}
    const {promise, callback} = this._connection.promisify(cb)
    toForm({
      configuration,
      dependencies,
      source
    }, (err, req) => err ? callback(err) : (
      this._api.request({
        method: 'PUT',
        path: '/foxx/service',
        rawBody: req.body,
        qs: {...qs, mount},
        headers: req.headers
      }, (err, res) => err ? callback(err) : callback(null, res.body))
    ))
    return promise
  }

  uninstallService (mount, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    this._api.delete(
      '/foxx/service',
      {...opts, mount},
      (err) => err ? callback(err) : callback(null)
    )
    return promise
  }

  getService (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/foxx/service',
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  getServiceConfiguration (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/foxx/configuration',
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  updateServiceConfiguration (mount, cfg, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.patch(
      '/foxx/configuration',
      cfg,
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  replaceServiceConfiguration (mount, cfg, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.put(
      '/foxx/configuration',
      cfg,
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  getServiceDependencies (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/foxx/dependencies',
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  updateServiceDependencies (mount, cfg, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.patch(
      '/foxx/dependencies',
      cfg,
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  replaceServiceDependencies (mount, cfg, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.put(
      '/foxx/dependencies',
      cfg,
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  enableServiceDevelopmentMode (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.post(
      '/foxx/development',
      undefined,
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  disableServiceDevelopmentMode (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.delete(
      '/foxx/development',
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  listServiceScripts (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/foxx/scripts',
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  runServiceScript (mount, name, args, cb) {
    if (typeof args === 'function') {
      cb = args
      args = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    this._api.post(
      `/foxx/scripts/${name}`,
      args,
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  runServiceTests (mount, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    this._api.post(
      '/foxx/tests',
      undefined,
      {...opts, mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  getServiceReadme (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/foxx/readme',
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  getServiceDocumentation (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.get(
      '/foxx/swagger',
      {mount},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  downloadService (mount, cb) {
    const {promise, callback} = this._connection.promisify(cb)
    this._api.request(
      {method: 'POST', path: '/foxx/download', qs: {mount}, expectBinary: true},
      (err, res) => err ? callback(err) : callback(null, res.body)
    )
    return promise
  }

  commitLocalServiceState (replace, cb) {
    if (typeof replace === 'function') {
      cb = replace
      replace = undefined
    }
    const {promise, callback} = this._connection.promisify(cb)
    this._api.post(
      '/foxx/commit',
      undefined,
      {replace},
      (err, res) => err ? callback(err) : callback(null)
    )
    return promise
  }
}
<|endoftext|>


<commit_before>import moment from 'moment';

export const convertToLocalDate = (dateToConvert) => {
    // convert date to local date, need to replace the space with a T for Date() formatting
    // Add a Z to the end to imply the date is in UTC
    const formattedDate = `${dateToConvert.replace(" ", "T")}Z`;
    const tmpDate = new Date(formattedDate);

    // format date as YYYY-MM-DD
    const year = tmpDate.getFullYear();
    let month = tmpDate.getMonth() + 1;
    if (month < 10) {
        month = `0${month}`;
    }
    let day = tmpDate.getDate();
    if (day < 10) {
        day = `0${day}`;
    }
    return `${year}-${month}-${day}`;
};

export const quarterToMonth = (quarter, quarterYear, type) => {
    // convert quarters to months
    const startMonth = ["10", "01", "04", "07"];
    const endMonth = ["12", "03", "06", "09"];

    const normalQuarter = parseInt(quarter, 10);

    let month = startMonth[normalQuarter - 1];

    if (type === "end") {
        month = endMonth[normalQuarter - 1];
    }

    let year = parseInt(quarterYear, 10);
    if (normalQuarter === 1) {
        // decrement the year by one for the first quarter of the fiscal year
        year -= 1;
    }

    return `${month}/${year}`;
};

export const previousQuarterMonth = (type) => {
    // gets the first or last month + year of the previous quarter.
    const currDate = moment().subtract(3, 'months');
    const month = currDate.month();
    let year = currDate.year();

    let quarter = 1;

    if (month >= 9) {
        // Add a year for quarter 1
        year += 1;
    }
    else if (month >= 6) {
        quarter = 4;
    }
    else if (month >= 3) {
        quarter = 3;
    }
    else {
        quarter = 2;
    }

    return quarterToMonth(quarter, year, type);
};

export const fyStartDate = () => {
    const month = parseInt(moment().format("M"), 10);
    let year = moment().format('YYYY');

    if (month >= 10) {
        year = moment().add(1, 'years').format('YYYY');
    }

    return quarterToMonth(1, year, 'start');
};

export const validUploadFileChecker = (rawFile) => {
    if (rawFile.file) {
        const fileName = rawFile.file.name.toLowerCase();
        const parsedFileName = fileName.indexOf('.') !== -1 ? fileName.split('.') : ['invalid'];
        const fileType = parsedFileName[parsedFileName.length - 1];
        return !!((fileType === 'csv' || fileType === 'txt'));
    }
    return 'unset';
};

export const earliestFileAYear = 2017;

export const convertDateToQuarter = (date) => {
    // Returns the fiscal quarter that the date falls in
    let quarter = 0;
    const month = moment(date).month();

    if (month >= 9 && month <= 11) {
        quarter = 1;
    }

    else if (month >= 0 && month <= 2) {
        quarter = 2;
    }

    else if (month >= 3 && month <= 5) {
        quarter = 3;
    }
    else if (month >= 6 && month <= 8) {
        quarter = 4;
    }

    return quarter;
};

export const currentFiscalYear = () => {
    // determine the current fiscal year
    const currentMonth = moment().month();
    let currentFY = moment().year();
    if (currentMonth >= 9) {
        // months are zero-indexed, so 9 is October
        // starting in October we are in the next fiscal year
        currentFY = moment().year() + 1;
    }

    return currentFY;
};

export const convertQuarterToPeriod = (quarter) => {
    const periodtoQuarters = {
        1: 3, // Quarter 1
        2: 6, // Quarter 2
        3: 9, // Quarter 3
        4: 12 // Quarter 4
    };
    return periodtoQuarters[quarter];
};

export const getPeriodTextFromValue = (value) => {
    const periodDictionary = {
        4: "04 - January",
        5: "05 - February",
        6: "06 - March",
        7: "07 - April",
        8: "08 - May",
        9: "09 - June",
        10: "10 - July",
        11: "11 - August",
        12: "12 - September",
        1: "01 - October",
        2: "02 - November",
        3: "03 - December"
    };
    return periodDictionary[value];
};
<commit_msg>[dev-2884] creates function that executes cb based  on keyCodes from event<commit_after>import moment from 'moment';

export const convertToLocalDate = (dateToConvert) => {
    // convert date to local date, need to replace the space with a T for Date() formatting
    // Add a Z to the end to imply the date is in UTC
    const formattedDate = `${dateToConvert.replace(" ", "T")}Z`;
    const tmpDate = new Date(formattedDate);

    // format date as YYYY-MM-DD
    const year = tmpDate.getFullYear();
    let month = tmpDate.getMonth() + 1;
    if (month < 10) {
        month = `0${month}`;
    }
    let day = tmpDate.getDate();
    if (day < 10) {
        day = `0${day}`;
    }
    return `${year}-${month}-${day}`;
};

export const quarterToMonth = (quarter, quarterYear, type) => {
    // convert quarters to months
    const startMonth = ["10", "01", "04", "07"];
    const endMonth = ["12", "03", "06", "09"];

    const normalQuarter = parseInt(quarter, 10);

    let month = startMonth[normalQuarter - 1];

    if (type === "end") {
        month = endMonth[normalQuarter - 1];
    }

    let year = parseInt(quarterYear, 10);
    if (normalQuarter === 1) {
        // decrement the year by one for the first quarter of the fiscal year
        year -= 1;
    }

    return `${month}/${year}`;
};

export const previousQuarterMonth = (type) => {
    // gets the first or last month + year of the previous quarter.
    const currDate = moment().subtract(3, 'months');
    const month = currDate.month();
    let year = currDate.year();

    let quarter = 1;

    if (month >= 9) {
        // Add a year for quarter 1
        year += 1;
    }
    else if (month >= 6) {
        quarter = 4;
    }
    else if (month >= 3) {
        quarter = 3;
    }
    else {
        quarter = 2;
    }

    return quarterToMonth(quarter, year, type);
};

export const fyStartDate = () => {
    const month = parseInt(moment().format("M"), 10);
    let year = moment().format('YYYY');

    if (month >= 10) {
        year = moment().add(1, 'years').format('YYYY');
    }

    return quarterToMonth(1, year, 'start');
};

export const validUploadFileChecker = (rawFile) => {
    if (rawFile.file) {
        const fileName = rawFile.file.name.toLowerCase();
        const parsedFileName = fileName.indexOf('.') !== -1 ? fileName.split('.') : ['invalid'];
        const fileType = parsedFileName[parsedFileName.length - 1];
        return !!((fileType === 'csv' || fileType === 'txt'));
    }
    return 'unset';
};

export const earliestFileAYear = 2017;

export const convertDateToQuarter = (date) => {
    // Returns the fiscal quarter that the date falls in
    let quarter = 0;
    const month = moment(date).month();

    if (month >= 9 && month <= 11) {
        quarter = 1;
    }

    else if (month >= 0 && month <= 2) {
        quarter = 2;
    }

    else if (month >= 3 && month <= 5) {
        quarter = 3;
    }
    else if (month >= 6 && month <= 8) {
        quarter = 4;
    }

    return quarter;
};

export const currentFiscalYear = () => {
    // determine the current fiscal year
    const currentMonth = moment().month();
    let currentFY = moment().year();
    if (currentMonth >= 9) {
        // months are zero-indexed, so 9 is October
        // starting in October we are in the next fiscal year
        currentFY = moment().year() + 1;
    }

    return currentFY;
};

export const convertQuarterToPeriod = (quarter) => {
    const periodtoQuarters = {
        1: 3, // Quarter 1
        2: 6, // Quarter 2
        3: 9, // Quarter 3
        4: 12 // Quarter 4
    };
    return periodtoQuarters[quarter];
};

export const getPeriodTextFromValue = (value) => {
    const periodDictionary = {
        4: "04 - January",
        5: "05 - February",
        6: "06 - March",
        7: "07 - April",
        8: "08 - May",
        9: "09 - June",
        10: "10 - July",
        11: "11 - August",
        12: "12 - September",
        1: "01 - October",
        2: "02 - November",
        3: "03 - December"
    };
    return periodDictionary[value];
};

export const createOnKeyDownHandler = (cb, args = [], keyCodes = [13, 32]) => (e) => { // 13 enter; 32 space
    if (keyCodes.includes(e.keyCode)) {
        cb(...args);
    }
};
<|endoftext|>


<commit_before>import { Class } from '../../core/Core';
import '../../core/Number';  // For Number#limit
import { bind } from '../../foundation/Binding';
import '../../foundation/ComputedProps';  // For Function#property, #nocache
import '../../foundation/RunLoop';  // For Function#queue
import ScrollView from '../containers/ScrollView';
import View from '../View';
import ViewEventsController from '../ViewEventsController';

const ListKBFocusView = Class({

    Extends: View,

    selection: null,
    singleSelection: null,

    index: bind( 'singleSelection*index' ),
    record: bind( 'singleSelection*record' ),

    itemHeight: 32,

    keys: {
        j: 'goNext',
        k: 'goPrev',
        x: 'select',
        X: 'select',
        o: 'trigger',
        Enter: 'trigger',
        s: 'star',
    },

    className: 'v-ListKBFocus',

    positioning: 'absolute',

    layoutIndex: function () {
        const index = this.get( 'index' );
        const list = this.get( 'singleSelection' ).get( 'content' );
        if ( index > -1 && list &&
                list.getObjectAt( index ) !== this.get( 'record' ) ) {
            return -1;
        }
        return index;
    }.property( 'index', 'record' ),

    layout: function () {
        const itemHeight = this.get( 'itemHeight' );
        const index = this.get( 'layoutIndex' );
        return {
            top: index < 0 ? 0 : itemHeight * index,
            left: index < 0 ? 'auto' : null,
            right: index < 0 ? '100%' : null,
            height: itemHeight,
        };
    }.property( 'itemHeight', 'layoutIndex' ),

    didEnterDocument () {
        const keys = this.get( 'keys' );
        const shortcuts = ViewEventsController.kbShortcuts;
        for ( const key in keys ) {
            shortcuts.register( key, this, keys[ key ] );
        }
        this.checkInitialScroll();
        return ListKBFocusView.parent.didEnterDocument.call( this );
    },
    willLeaveDocument () {
        const keys = this.get( 'keys' );
        const shortcuts = ViewEventsController.kbShortcuts;
        for ( const key in keys ) {
            shortcuts.deregister( key, this, keys[ key ] );
        }
        return ListKBFocusView.parent.willLeaveDocument.call( this );
    },

    // Scroll to centre widget on screen with no animation
    checkInitialScroll: function () {
        if ( this.get( 'distanceFromVisRect' ) ) {
            this.scrollIntoView( 0, false );
        }
    }.nextFrame(),

    checkScroll: function () {
        const distance = this.get( 'distanceFromVisRect' );
        if ( distance ) {
            this.scrollIntoView( distance < 0 ? -0.6 : 0.6, true );
        }
    }.nextFrame().observes( 'record' ),

    distanceFromVisRect: function () {
        const layoutIndex = this.get( 'layoutIndex' );
        const scrollView = this.getParent( ScrollView );
        if ( scrollView && layoutIndex > -1 && this.get( 'isInDocument' ) ) {
            const scrollTop = scrollView.get( 'scrollTop' );
            const position = this.getPositionRelativeTo( scrollView );
            const top = position.top;
            const above = top - scrollTop;

            if ( above < 0 ) {
                return above;
            }

            const scrollHeight = scrollView.get( 'pxHeight' );
            const below = top + this.get( 'pxHeight' ) -
                scrollTop - scrollHeight;

            if ( below > 0 ) {
                return below;
            }
        }
        return 0;
    }.property().nocache(),

    scrollIntoView ( offset, withAnimation ) {
        const scrollView = this.getParent( ScrollView );
        if ( scrollView ) {
            const scrollHeight = scrollView.get( 'pxHeight' );
            const itemHeight = this.get( 'pxHeight' );
            const top = this.getPositionRelativeTo( scrollView ).top;

            if ( offset && -1 <= offset && offset <= 1 ) {
                offset = ( offset * ( scrollHeight - itemHeight ) ) >> 1;
            }
            scrollView.scrollTo( 0,
                Math.max( 0,
                    top +
                    ( ( itemHeight - scrollHeight ) >> 1 ) +
                    ( offset || 0 )
                ),
                withAnimation
            );
        }
    },

    go ( delta ) {
        const index = this.get( 'index' );
        const singleSelection = this.get( 'singleSelection' );
        const list = singleSelection.get( 'content' );
        const length = list && list.get( 'length' ) || 0;
        if ( delta === 1 && index > -1 && list &&
                list.getObjectAt( index ) !== this.get( 'record' ) ) {
            delta = 0;
        }
        if ( delta ) {
            singleSelection.set( 'index',
                ( index + delta ).limit( 0, length - 1 ) );
        } else {
            singleSelection.propertyDidChange( 'index' );
        }
    },
    goNext () {
        this.go( 1 );
    },
    goPrev () {
        this.go( -1 );
    },
    select ( event ) {
        const index = this.get( 'index' );
        const selection = this.get( 'selection' );
        const record = this.get( 'record' );
        // Check it's next to a loaded record.
        if ( selection && record ) {
            selection.selectIndex( index,
                !selection.isStoreKeySelected( record.get( 'storeKey' ) ),
                event.shiftKey );
        }
    },
    trigger () {},
    star () {},
});

export default ListKBFocusView;
<commit_msg>Use visibility:hidden to hide ListKBFocusView<commit_after>import { Class } from '../../core/Core';
import '../../core/Number';  // For Number#limit
import { bind } from '../../foundation/Binding';
import '../../foundation/ComputedProps';  // For Function#property, #nocache
import '../../foundation/RunLoop';  // For Function#queue
import ScrollView from '../containers/ScrollView';
import View from '../View';
import ViewEventsController from '../ViewEventsController';

const ListKBFocusView = Class({

    Extends: View,

    selection: null,
    singleSelection: null,

    index: bind( 'singleSelection*index' ),
    record: bind( 'singleSelection*record' ),

    itemHeight: 32,

    keys: {
        j: 'goNext',
        k: 'goPrev',
        x: 'select',
        X: 'select',
        o: 'trigger',
        Enter: 'trigger',
        s: 'star',
    },

    className: 'v-ListKBFocus',

    positioning: 'absolute',

    layoutIndex: function () {
        const index = this.get( 'index' );
        const list = this.get( 'singleSelection' ).get( 'content' );
        if ( index > -1 && list &&
                list.getObjectAt( index ) !== this.get( 'record' ) ) {
            return -1;
        }
        return index;
    }.property( 'index', 'record' ),

    layout: function () {
        const itemHeight = this.get( 'itemHeight' );
        const index = this.get( 'layoutIndex' );
        return {
            visibility: index < 0 ? 'hidden' : 'visible',
            top: index < 0 ? 0 : itemHeight * index,
            height: itemHeight,
        };
    }.property( 'itemHeight', 'layoutIndex' ),

    didEnterDocument () {
        const keys = this.get( 'keys' );
        const shortcuts = ViewEventsController.kbShortcuts;
        for ( const key in keys ) {
            shortcuts.register( key, this, keys[ key ] );
        }
        this.checkInitialScroll();
        return ListKBFocusView.parent.didEnterDocument.call( this );
    },
    willLeaveDocument () {
        const keys = this.get( 'keys' );
        const shortcuts = ViewEventsController.kbShortcuts;
        for ( const key in keys ) {
            shortcuts.deregister( key, this, keys[ key ] );
        }
        return ListKBFocusView.parent.willLeaveDocument.call( this );
    },

    // Scroll to centre widget on screen with no animation
    checkInitialScroll: function () {
        if ( this.get( 'distanceFromVisRect' ) ) {
            this.scrollIntoView( 0, false );
        }
    }.nextFrame(),

    checkScroll: function () {
        const distance = this.get( 'distanceFromVisRect' );
        if ( distance ) {
            this.scrollIntoView( distance < 0 ? -0.6 : 0.6, true );
        }
    }.nextFrame().observes( 'record' ),

    distanceFromVisRect: function () {
        const layoutIndex = this.get( 'layoutIndex' );
        const scrollView = this.getParent( ScrollView );
        if ( scrollView && layoutIndex > -1 && this.get( 'isInDocument' ) ) {
            const scrollTop = scrollView.get( 'scrollTop' );
            const position = this.getPositionRelativeTo( scrollView );
            const top = position.top;
            const above = top - scrollTop;

            if ( above < 0 ) {
                return above;
            }

            const scrollHeight = scrollView.get( 'pxHeight' );
            const below = top + this.get( 'pxHeight' ) -
                scrollTop - scrollHeight;

            if ( below > 0 ) {
                return below;
            }
        }
        return 0;
    }.property().nocache(),

    scrollIntoView ( offset, withAnimation ) {
        const scrollView = this.getParent( ScrollView );
        if ( scrollView ) {
            const scrollHeight = scrollView.get( 'pxHeight' );
            const itemHeight = this.get( 'pxHeight' );
            const top = this.getPositionRelativeTo( scrollView ).top;

            if ( offset && -1 <= offset && offset <= 1 ) {
                offset = ( offset * ( scrollHeight - itemHeight ) ) >> 1;
            }
            scrollView.scrollTo( 0,
                Math.max( 0,
                    top +
                    ( ( itemHeight - scrollHeight ) >> 1 ) +
                    ( offset || 0 )
                ),
                withAnimation
            );
        }
    },

    go ( delta ) {
        const index = this.get( 'index' );
        const singleSelection = this.get( 'singleSelection' );
        const list = singleSelection.get( 'content' );
        const length = list && list.get( 'length' ) || 0;
        if ( delta === 1 && index > -1 && list &&
                list.getObjectAt( index ) !== this.get( 'record' ) ) {
            delta = 0;
        }
        if ( delta ) {
            singleSelection.set( 'index',
                ( index + delta ).limit( 0, length - 1 ) );
        } else {
            singleSelection.propertyDidChange( 'index' );
        }
    },
    goNext () {
        this.go( 1 );
    },
    goPrev () {
        this.go( -1 );
    },
    select ( event ) {
        const index = this.get( 'index' );
        const selection = this.get( 'selection' );
        const record = this.get( 'record' );
        // Check it's next to a loaded record.
        if ( selection && record ) {
            selection.selectIndex( index,
                !selection.isStoreKeySelected( record.get( 'storeKey' ) ),
                event.shiftKey );
        }
    },
    trigger () {},
    star () {},
});

export default ListKBFocusView;
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react';
import {withRouter, Link} from 'react-router';
import {getKapacitor, deleteSource} from 'shared/apis';
import {
  loadSources as loadSourcesAction,
  removeSource as removeSourceAction,
} from 'src/shared/actions/sources';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';

export const ManageSources = React.createClass({
  propTypes: {
    location: PropTypes.shape({
      pathname: PropTypes.string.isRequired,
    }).isRequired,
    source: PropTypes.shape({
      id: PropTypes.string.isRequired,
      links: PropTypes.shape({
        proxy: PropTypes.string.isRequired,
        self: PropTypes.string.isRequired,
      }),
    }),
    sources: PropTypes.array,
    addFlashMessage: PropTypes.func,
    loadSourcesAction: PropTypes.func.isRequired,
    removeSourceAction: PropTypes.func.isRequired,
  },
  getInitialState() {
    return {
      kapacitors: {},
    };
  },

  componentDidMount() {
    this.props.sources.forEach((source) => {
      const kapacitors = {};
      getKapacitor(source).then((kapacitor) => {
        kapacitors[source.id] = kapacitor;
      });
      this.setState(kapacitors);
    });
  },

  handleDeleteSource(source) {
    const {addFlashMessage} = this.props;

    deleteSource(source).then(() => {
      removeSourceAction(source);
      addFlashMessage({type: 'success', text: 'Source removed from Chronograf'});
    }).catch(() => {
      addFlashMessage({type: 'error', text: 'Could not remove source from Chronograf'});
    });
  },

  render() {
    const {kapacitors} = this.state;
    const {sources} = this.props;
    const {pathname} = this.props.location;
    const numSources = sources ? sources.length : 0;
    const sourcesTitle = `${numSources} ${numSources === 1 ? 'Source' : 'Sources'}`;

    return (

InfluxDB Sources

{sourcesTitle}

                    <Link to={`/sources/${this.props.source.id}/manage-sources/new`} className="btn btn-sm btn-primary">Add New Source</Link>

                            Name
                            Host
                            Kapacitor

                          {
                            sources.map((source) => {
                              const kapacitorName = kapacitors[source.id] ? kapacitors[source.id].name : '';
                              return (

                                  {source.name}{source.default ? Default : null}
                                  {source.url}
                                  {kapacitorName}

                                    <Link className="btn btn-info btn-xs" to={`${pathname}/${source.id}/edit`}></Link>
                                    <Link className="btn btn-success btn-xs" to={`/sources/${source.id}/hosts`}>Connect</Link>
                                    <button className="btn btn-danger btn-xs" onClick={() => this.handleDeleteSource(source)}></button>

                              );
                            })
                          }

    );
  },
});

function mapStateToProps(state) {
  return {
    sources: state.sources,
  };
}

function mapDispatchToProps(dispatch) {
  return {
    loadSourcesAction: bindActionCreators(loadSourcesAction, dispatch),
    removeSourceAction: bindActionCreators(removeSourceAction, dispatch),
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(withRouter(ManageSources));
<commit_msg>revert unnecessary change<commit_after>import React, {PropTypes} from 'react';
import {withRouter, Link} from 'react-router';
import {getKapacitor, deleteSource} from 'shared/apis';
import {
  loadSources as loadSourcesAction,
  removeSource as removeSourceAction,
} from 'src/shared/actions/sources';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';

export const ManageSources = React.createClass({
  propTypes: {
    location: PropTypes.shape({
      pathname: PropTypes.string.isRequired,
    }).isRequired,
    source: PropTypes.shape({
      id: PropTypes.string.isRequired,
      links: PropTypes.shape({
        proxy: PropTypes.string.isRequired,
        self: PropTypes.string.isRequired,
      }),
    }),
    sources: PropTypes.array,
    addFlashMessage: PropTypes.func,
    loadSourcesAction: PropTypes.func.isRequired,
    removeSourceAction: PropTypes.func.isRequired,
  },
  getInitialState() {
    return {
      kapacitors: {},
    };
  },

  componentDidMount() {
    this.props.sources.forEach((source) => {
      const kapacitors = {};
      getKapacitor(source).then((kapacitor) => {
        kapacitors[source.id] = kapacitor;
      });
      this.setState(kapacitors);
    });
  },

  handleDeleteSource(source) {
    const {addFlashMessage} = this.props;

    deleteSource(source).then(() => {
      removeSourceAction(source);
      addFlashMessage({type: 'success', text: 'Source removed from Chronograf'});
    }).catch(() => {
      addFlashMessage({type: 'error', text: 'Could not remove source from Chronograf'});
    });
  },

  render() {
    const {kapacitors} = this.state;
    const {sources} = this.props;
    const {pathname} = this.props.location;
    const numSources = sources.length;
    const sourcesTitle = `${numSources} ${numSources === 1 ? 'Source' : 'Sources'}`;

    return (

InfluxDB Sources

{sourcesTitle}

                    <Link to={`/sources/${this.props.source.id}/manage-sources/new`} className="btn btn-sm btn-primary">Add New Source</Link>

                            Name
                            Host
                            Kapacitor

                          {
                            sources.map((source) => {
                              const kapacitorName = kapacitors[source.id] ? kapacitors[source.id].name : '';
                              return (

                                  {source.name}{source.default ? Default : null}
                                  {source.url}
                                  {kapacitorName}

                                    <Link className="btn btn-info btn-xs" to={`${pathname}/${source.id}/edit`}></Link>
                                    <Link className="btn btn-success btn-xs" to={`/sources/${source.id}/hosts`}>Connect</Link>
                                    <button className="btn btn-danger btn-xs" onClick={() => this.handleDeleteSource(source)}></button>

                              );
                            })
                          }

    );
  },
});

function mapStateToProps(state) {
  return {
    sources: state.sources,
  };
}

function mapDispatchToProps(dispatch) {
  return {
    loadSourcesAction: bindActionCreators(loadSourcesAction, dispatch),
    removeSourceAction: bindActionCreators(removeSourceAction, dispatch),
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(withRouter(ManageSources));
<|endoftext|>


<commit_before>const gulp = require('gulp')
const del = require('del')
const shell = require('gulp-shell')
const typedoc = require('gulp-typedoc')
const mocha = require('gulp-mocha')
const tslint = require('gulp-tslint')
const format = require('gulp-typescript-formatter')
const pages = require('gh-pages')
const coveralls = require('gulp-coveralls')

const distFiles = require('./package.json').files

const SOURCE_FILES = ["./src/*.ts", "./src/*.tsx"]
const TEST_FILES = ["./src/**/*.test.ts", "./src/**/*.test.tsx"]
const PUBLIC_SOURCE_FILES = ["./src/*.ts", "./src/*.tsx"]
const PRIVATE_INTERFACE_FILES = ["./src/lib/**/*.d.ts", "./src/cli/**/*.d.ts"]

/** Test / Lint */

gulp.task('lint', function () {
  return gulp.src(SOURCE_FILES)
    .pipe(tslint())
})

gulp.task('test', function () {
  return gulp.src(TEST_FILES)
    .pipe(mocha())
})

gulp.task('typecheck', shell.task([
  'node_modules/.bin/tsc --noemit'
]))

/** Autofixing & formatting */

gulp.task('format', function () {
  return gulp.src(SOURCE_FILES)
    .pipe(format({
        baseDir: '.',
        tslint: true,
        editorconfig: true
    }))
    .pipe(gulp.dest('src'))
})

gulp.task('lint:fix', function () {
  return gulp.src(SOURCE_FILES)
    .pipe(tslint({ fix: true }))
})

gulp.task('fix', gulp.series('format', 'lint:fix'))

/** Coverage */

gulp.task('coverage:clean', function () {
  return del('./coverage/**/*')
})

gulp.task('coverage:run', shell.task([
  'node_modules/.bin/nyc --reporter html --reporter lcov --extension .ts --extension .tsx --include src/lib --exclude src/**/*.test.ts --exclude src/**/*.test.tsx --all node_modules/.bin/mocha'
]))

gulp.task('coverage', gulp.series('coverage:clean', 'coverage:run'))

gulp.task('coverage:submit', function () {
  return gulp.src('./coverage/lcov.info').pipe(coveralls())
})

/**
 * Site generator
 */

gulp.task('site:bootstrap', shell.task(['npm install'], { cwd: './docs-site' }))

gulp.task('site:typedoc', function () {
  return gulp.src(PUBLIC_SOURCE_FILES)
    .pipe(typedoc({
      json: 'docs-site/docs.json',
      module: 'commonjs',
      tsconfig: 'tsconfig.json'
    }))
})

gulp.task('site:build', shell.task(['npm run build'], { cwd: './docs-site' }))

gulp.task('site:build:watch', shell.task(['npm run develop'], { cwd: './docs-site' }))

gulp.task('site', gulp.series(['site:bootstrap', 'site:typedoc', 'site:build']))

gulp.task('site:ci', gulp.series('site', function (done) {
  pages.clean()
  pages.publish('./docs-site/public', undefined, done)
}))

gulp.task('site:watch', gulp.series(
  'site:typedoc',
  gulp.parallel('site:build:watch'),
))

/** Build */

gulp.task('build:clean', function () {
  return del([...distFiles, './.build'])
});

gulp.task('build:transpile', shell.task(['node_modules/.bin/tsc --outDir .']))

gulp.task('build:delete-private-interfaces', function () {
  return del(PRIVATE_INTERFACE_FILES);
})

gulp.task('rebuild', gulp.series([
  'build:transpile',
  'build:delete-private-interfaces'
]))

gulp.task('watch:rebuild', function () {
  return gulp.watch(SOURCE_FILES, gulp.parallel(['rebuild']))
})

gulp.task('build', gulp.series(
  'build:clean',
  'build:transpile',
  'build:delete-private-interfaces'
))

/** CI entry */

gulp.task('default', gulp.series([
  'fix',
  gulp.parallel(
    'coverage',
    'typecheck'
  )
]))

gulp.task('ci', gulp.series(
  gulp.parallel(
    'lint',
    'coverage',
    'typecheck',
    'site'
  ),
  'coverage:submit'
))

gulp.task('watch', function () {
  return gulp.watch(SOURCE_FILES, gulp.parallel(['default']))
})
<commit_msg>Workaround for typedoc flakiness<commit_after>const gulp = require('gulp')
const del = require('del')
const shell = require('gulp-shell')
const typedoc = require('gulp-typedoc')
const mocha = require('gulp-mocha')
const tslint = require('gulp-tslint')
const format = require('gulp-typescript-formatter')
const pages = require('gh-pages')
const coveralls = require('gulp-coveralls')

const distFiles = require('./package.json').files

const SOURCE_FILES = ["./src/*.ts", "./src/*.tsx"]
const TEST_FILES = ["./src/**/*.test.ts", "./src/**/*.test.tsx"]
const PUBLIC_SOURCE_FILES = ["./src/*.ts", "./src/*.tsx"]
const PRIVATE_INTERFACE_FILES = ["./src/lib/**/*.d.ts", "./src/cli/**/*.d.ts"]

/** Test / Lint */

gulp.task('lint', function () {
  return gulp.src(SOURCE_FILES)
    .pipe(tslint())
})

gulp.task('test', function () {
  return gulp.src(TEST_FILES)
    .pipe(mocha())
})

gulp.task('typecheck', shell.task([
  'node_modules/.bin/tsc --noemit'
]))

/** Autofixing & formatting */

gulp.task('format', function () {
  return gulp.src(SOURCE_FILES)
    .pipe(format({
        baseDir: '.',
        tslint: true,
        editorconfig: true
    }))
    .pipe(gulp.dest('src'))
})

gulp.task('lint:fix', function () {
  return gulp.src(SOURCE_FILES)
    .pipe(tslint({ fix: true }))
})

gulp.task('fix', gulp.series('format', 'lint:fix'))

/** Coverage */

gulp.task('coverage:clean', function () {
  return del('./coverage/**/*')
})

gulp.task('coverage:run', shell.task([
  'node_modules/.bin/nyc --reporter html --reporter lcov --extension .ts --extension .tsx --include src/lib --exclude src/**/*.test.ts --exclude src/**/*.test.tsx --all node_modules/.bin/mocha'
]))

gulp.task('coverage', gulp.series('coverage:clean', 'coverage:run'))

gulp.task('coverage:submit', function () {
  return gulp.src('./coverage/lcov.info').pipe(coveralls())
})

/**
 * Site generator
 */

gulp.task('site:bootstrap', shell.task(['npm install'], { cwd: './docs-site' }))

gulp.task('site:typedoc', function () {
  return gulp.src(PUBLIC_SOURCE_FILES)
    .pipe(typedoc({
      json: 'docs-site/docs.json',
      module: 'commonjs',
      tsconfig: 'tsconfig.json',
      ignoreCompilerErrors: true
    }))
})

gulp.task('site:build', shell.task(['npm run build'], { cwd: './docs-site' }))

gulp.task('site:build:watch', shell.task(['npm run develop'], { cwd: './docs-site' }))

gulp.task('site', gulp.series(['site:bootstrap', 'site:typedoc', 'site:build']))

gulp.task('site:ci', gulp.series('site', function (done) {
  pages.clean()
  pages.publish('./docs-site/public', undefined, done)
}))

gulp.task('site:watch', gulp.series(
  'site:typedoc',
  gulp.parallel('site:build:watch'),
))

/** Build */

gulp.task('build:clean', function () {
  return del([...distFiles, './.build'])
});

gulp.task('build:transpile', shell.task(['node_modules/.bin/tsc --outDir .']))

gulp.task('build:delete-private-interfaces', function () {
  return del(PRIVATE_INTERFACE_FILES);
})

gulp.task('rebuild', gulp.series([
  'build:transpile',
  'build:delete-private-interfaces'
]))

gulp.task('watch:rebuild', function () {
  return gulp.watch(SOURCE_FILES, gulp.parallel(['rebuild']))
})

gulp.task('build', gulp.series(
  'build:clean',
  'build:transpile',
  'build:delete-private-interfaces'
))

/** CI entry */

gulp.task('default', gulp.series([
  'fix',
  gulp.parallel(
    'coverage',
    'typecheck'
  )
]))

gulp.task('ci', gulp.series(
  gulp.parallel(
    'lint',
    'coverage',
    'typecheck',
    'site'
  ),
  'coverage:submit'
))

gulp.task('watch', function () {
  return gulp.watch(SOURCE_FILES, gulp.parallel(['default']))
})
<|endoftext|>


<commit_before>/*
 * Fuel UX Datagrid
 * https://github.com/ExactTarget/fuelux
 *
 * Copyright (c) 2012 ExactTarget
 * Licensed under the MIT license.
 */

define(function(require) {

	var $ = require('jquery');

	// DATAGRID CONSTRUCTOR AND PROTOTYPE

	var Datagrid = function (element, options) {
		this.$element = $(element);
		this.$thead = this.$element.find('thead');
		this.$tfoot = this.$element.find('tfoot');
		this.$footer = this.$element.find('tfoot th');
		this.$footerchildren = this.$footer.children().show().css('visibility', 'hidden');
		this.$topheader = this.$element.find('thead th');
		this.$searchcontrol = this.$element.find('.search');
		this.$filtercontrol = this.$element.find('.filter');
		this.$pagesize = this.$element.find('.grid-pagesize');
		this.$pageinput = this.$element.find('.grid-pager input');
		this.$pagedropdown = this.$element.find('.grid-pager .dropdown-menu');
		this.$prevpagebtn = this.$element.find('.grid-prevpage');
		this.$nextpagebtn = this.$element.find('.grid-nextpage');
		this.$pageslabel = this.$element.find('.grid-pages');
		this.$countlabel = this.$element.find('.grid-count');
		this.$startlabel = this.$element.find('.grid-start');
		this.$endlabel = this.$element.find('.grid-end');

		this.$tbody = $('
').insertAfter(this.$thead);
		this.$colheader = $('
').appendTo(this.$thead);

		this.options = $.extend(true, {}, $.fn.datagrid.defaults, options);

		if(this.$pagesize.hasClass('select')) {
			this.options.dataOptions.pageSize = parseInt(this.$pagesize.select('selectedItem').value, 10);
		} else {
			this.options.dataOptions.pageSize = parseInt(this.$pagesize.val(), 10);
		}

		this.columns = this.options.dataSource.columns();

		this.$nextpagebtn.on('click', $.proxy(this.next, this));
		this.$prevpagebtn.on('click', $.proxy(this.previous, this));
		this.$searchcontrol.on('searched cleared', $.proxy(this.searchChanged, this));
		this.$filtercontrol.on('changed', $.proxy(this.filterChanged, this));
		this.$colheader.on('click', 'th', $.proxy(this.headerClicked, this));

		if(this.$pagesize.hasClass('select')) {
			this.$pagesize.on('changed', $.proxy(this.pagesizeChanged, this));
		} else {
			this.$pagesize.on('change', $.proxy(this.pagesizeChanged, this));
		}

		this.$pageinput.on('change', $.proxy(this.pageChanged, this));

		this.renderColumns();

		if (this.options.stretchHeight) this.initStretchHeight();

		this.renderData();
	};

	Datagrid.prototype = {

		constructor: Datagrid,

		renderColumns: function () {
			var self = this;

			this.$footer.attr('colspan', this.columns.length);
			this.$topheader.attr('colspan', this.columns.length);

			var colHTML = '';

			$.each(this.columns, function (index, column) {
				colHTML += '' + column.label + '	';
			});

			self.$colheader.append(colHTML);
		},

		updateColumns: function ($target, direction) {
			var className = (direction === 'asc') ? 'icon-chevron-up' : 'icon-chevron-down';
			this.$colheader.find('i').remove();
			this.$colheader.find('th').removeClass('sorted');
			$('').addClass(className).appendTo($target);
			$target.addClass('sorted');
		},

		updatePageDropdown: function (data) {
			var pageHTML = '';

			for (var i = 1; i <= data.pages; i++) {
				pageHTML += '
' + i + '
';
			}

			this.$pagedropdown.html(pageHTML);
		},

		updatePageButtons: function (data) {
			if (data.page === 1) {
				this.$prevpagebtn.attr('disabled', 'disabled');
			} else {
				this.$prevpagebtn.removeAttr('disabled');
			}

			if (data.page === data.pages) {
				this.$nextpagebtn.attr('disabled', 'disabled');
			} else {
				this.$nextpagebtn.removeAttr('disabled');
			}
		},

		renderData: function () {
			var self = this;

			this.$tbody.html(this.placeholderRowHTML(this.options.loadingHTML));

			this.options.dataSource.data(this.options.dataOptions, function (data) {
				var itemdesc = (data.count === 1) ? self.options.itemText : self.options.itemsText;
				var rowHTML = '';

				self.$footerchildren.css('visibility', function () {
					return (data.count > 0) ? 'visible' : 'hidden';
				});

				self.$pageinput.val(data.page);
				self.$pageslabel.text(data.pages);
				self.$countlabel.text(data.count + ' ' + itemdesc);
				self.$startlabel.text(data.start);
				self.$endlabel.text(data.end);

				self.updatePageDropdown(data);
				self.updatePageButtons(data);

				$.each(data.data, function (index, row) {
					rowHTML += '
';
					$.each(self.columns, function (index, column) {
						rowHTML += '' + row[column.property] + '	';
					});
					rowHTML += '
';
				});

				if (!rowHTML) rowHTML = self.placeholderRowHTML('0 ' + self.options.itemsText);

				self.$tbody.html(rowHTML);
				self.stretchHeight();

				self.$element.trigger('loaded');
			});

		},

		placeholderRowHTML: function (content) {
			return '
' + content + '
';
		},

		headerClicked: function (e) {
			var $target = $(e.target);
			if (!$target.hasClass('sortable')) return;

			var direction = this.options.dataOptions.sortDirection;
			var sort = this.options.dataOptions.sortProperty;
			var property = $target.data('property');

			if (sort === property) {
				this.options.dataOptions.sortDirection = (direction === 'asc') ? 'desc' : 'asc';
			} else {
				this.options.dataOptions.sortDirection = 'asc';
				this.options.dataOptions.sortProperty = property;
			}

			this.options.dataOptions.pageIndex = 0;
			this.updateColumns($target, this.options.dataOptions.sortDirection);
			this.renderData();
		},

		pagesizeChanged: function (e, pageSize) {
			if(pageSize) {
				this.options.dataOptions.pageSize = parseInt(pageSize.value, 10);
			} else {
				this.options.dataOptions.pageSize = parseInt($(e.target).val(), 10);
			}

			this.options.dataOptions.pageIndex = 0;
			this.renderData();
		},

		pageChanged: function (e) {
			var pageRequested = parseInt($(e.target).val(), 10);
			pageRequested = (isNaN(pageRequested)) ? 1 : pageRequested;
			var maxPages = this.$pageslabel.text();

			this.options.dataOptions.pageIndex =
				(pageRequested > maxPages) ? maxPages - 1 : pageRequested - 1;

			this.renderData();
		},

		searchChanged: function (e, search) {
			this.options.dataOptions.search = search;
			this.options.dataOptions.pageIndex = 0;
			this.renderData();
		},

		filterChanged: function (e, filter) {
			this.options.dataOptions.filter = filter;
			this.renderData();
		},

		previous: function () {
			this.options.dataOptions.pageIndex--;
			this.renderData();
		},

		next: function () {
			this.options.dataOptions.pageIndex++;
			this.renderData();
		},

		reload: function () {
			this.options.dataOptions.pageIndex = 0;
			this.renderData();
		},

		initStretchHeight: function () {
			this.$gridContainer = this.$element.parent();

			this.$element.wrap('
');
			this.$stretchWrapper = this.$element.parent();

			this.$headerTable = $('
').attr('class', this.$element.attr('class'));
			this.$footerTable = this.$headerTable.clone();

			this.$headerTable.prependTo(this.$gridContainer).addClass('datagrid-stretch-header');
			this.$thead.detach().appendTo(this.$headerTable);

			this.$sizingHeader = this.$thead.clone();
			this.$sizingHeader.find('tr:first').remove();

			this.$footerTable.appendTo(this.$gridContainer).addClass('datagrid-stretch-footer');
			this.$tfoot.detach().appendTo(this.$footerTable);
		},

		stretchHeight: function () {
			if (!this.$gridContainer) return;

			this.setColumnWidths();

			var targetHeight = this.$gridContainer.height();
			var headerHeight = this.$headerTable.outerHeight();
			var footerHeight = this.$footerTable.outerHeight();
			var overhead = headerHeight + footerHeight;

			this.$stretchWrapper.height(targetHeight - overhead);
		},

		setColumnWidths: function () {
			if (!this.$sizingHeader) return;

			this.$element.prepend(this.$sizingHeader);

			var $sizingCells = this.$sizingHeader.find('th');
			var columnCount = $sizingCells.length;

			function matchSizingCellWidth(i, el) {
				if (i === columnCount - 1) return;
				$(el).width($sizingCells.eq(i).width());
			}

			this.$colheader.find('th').each(matchSizingCellWidth);
			this.$tbody.find('tr:first > td').each(matchSizingCellWidth);

			this.$sizingHeader.detach();
		}
	};

	// DATAGRID PLUGIN DEFINITION

	$.fn.datagrid = function (option) {
		return this.each(function () {
			var $this = $(this);
			var data = $this.data('datagrid');
			var options = typeof option === 'object' && option;

			if (!data) $this.data('datagrid', (data = new Datagrid(this, options)));
			if (typeof option === 'string') data[option]();
		});
	};

	$.fn.datagrid.defaults = {
		dataOptions: { pageIndex: 0, pageSize: 10 },
		loadingHTML: '

',
		itemsText: 'items',
		itemText: 'item'
	};

	$.fn.datagrid.Constructor = Datagrid;

});
<commit_msg>Adds class support to columns<commit_after>/*
 * Fuel UX Datagrid
 * https://github.com/ExactTarget/fuelux
 *
 * Copyright (c) 2012 ExactTarget
 * Licensed under the MIT license.
 */

define(function(require) {

	var $ = require('jquery');

	// DATAGRID CONSTRUCTOR AND PROTOTYPE

	var Datagrid = function (element, options) {
		this.$element = $(element);
		this.$thead = this.$element.find('thead');
		this.$tfoot = this.$element.find('tfoot');
		this.$footer = this.$element.find('tfoot th');
		this.$footerchildren = this.$footer.children().show().css('visibility', 'hidden');
		this.$topheader = this.$element.find('thead th');
		this.$searchcontrol = this.$element.find('.search');
		this.$filtercontrol = this.$element.find('.filter');
		this.$pagesize = this.$element.find('.grid-pagesize');
		this.$pageinput = this.$element.find('.grid-pager input');
		this.$pagedropdown = this.$element.find('.grid-pager .dropdown-menu');
		this.$prevpagebtn = this.$element.find('.grid-prevpage');
		this.$nextpagebtn = this.$element.find('.grid-nextpage');
		this.$pageslabel = this.$element.find('.grid-pages');
		this.$countlabel = this.$element.find('.grid-count');
		this.$startlabel = this.$element.find('.grid-start');
		this.$endlabel = this.$element.find('.grid-end');

		this.$tbody = $('
').insertAfter(this.$thead);
		this.$colheader = $('
').appendTo(this.$thead);

		this.options = $.extend(true, {}, $.fn.datagrid.defaults, options);

		if(this.$pagesize.hasClass('select')) {
			this.options.dataOptions.pageSize = parseInt(this.$pagesize.select('selectedItem').value, 10);
		} else {
			this.options.dataOptions.pageSize = parseInt(this.$pagesize.val(), 10);
		}

		this.columns = this.options.dataSource.columns();

		this.$nextpagebtn.on('click', $.proxy(this.next, this));
		this.$prevpagebtn.on('click', $.proxy(this.previous, this));
		this.$searchcontrol.on('searched cleared', $.proxy(this.searchChanged, this));
		this.$filtercontrol.on('changed', $.proxy(this.filterChanged, this));
		this.$colheader.on('click', 'th', $.proxy(this.headerClicked, this));

		if(this.$pagesize.hasClass('select')) {
			this.$pagesize.on('changed', $.proxy(this.pagesizeChanged, this));
		} else {
			this.$pagesize.on('change', $.proxy(this.pagesizeChanged, this));
		}

		this.$pageinput.on('change', $.proxy(this.pageChanged, this));

		this.renderColumns();

		if (this.options.stretchHeight) this.initStretchHeight();

		this.renderData();
	};

	Datagrid.prototype = {

		constructor: Datagrid,

		renderColumns: function () {
			var self = this;

			this.$footer.attr('colspan', this.columns.length);
			this.$topheader.attr('colspan', this.columns.length);

			var colHTML = '';

			$.each(this.columns, function (index, column) {
				colHTML += '' + column.label + '	';
			});

			self.$colheader.append(colHTML);
		},

		updateColumns: function ($target, direction) {
			var className = (direction === 'asc') ? 'icon-chevron-up' : 'icon-chevron-down';
			this.$colheader.find('i').remove();
			this.$colheader.find('th').removeClass('sorted');
			$('').addClass(className).appendTo($target);
			$target.addClass('sorted');
		},

		updatePageDropdown: function (data) {
			var pageHTML = '';

			for (var i = 1; i <= data.pages; i++) {
				pageHTML += '
' + i + '
';
			}

			this.$pagedropdown.html(pageHTML);
		},

		updatePageButtons: function (data) {
			if (data.page === 1) {
				this.$prevpagebtn.attr('disabled', 'disabled');
			} else {
				this.$prevpagebtn.removeAttr('disabled');
			}

			if (data.page === data.pages) {
				this.$nextpagebtn.attr('disabled', 'disabled');
			} else {
				this.$nextpagebtn.removeAttr('disabled');
			}
		},

		renderData: function () {
			var self = this;

			this.$tbody.html(this.placeholderRowHTML(this.options.loadingHTML));

			this.options.dataSource.data(this.options.dataOptions, function (data) {
				var itemdesc = (data.count === 1) ? self.options.itemText : self.options.itemsText;
				var rowHTML = '';

				self.$footerchildren.css('visibility', function () {
					return (data.count > 0) ? 'visible' : 'hidden';
				});

				self.$pageinput.val(data.page);
				self.$pageslabel.text(data.pages);
				self.$countlabel.text(data.count + ' ' + itemdesc);
				self.$startlabel.text(data.start);
				self.$endlabel.text(data.end);

				self.updatePageDropdown(data);
				self.updatePageButtons(data);

				$.each(data.data, function (index, row) {
					rowHTML += '
';
					$.each(self.columns, function (index, column) {
						rowHTML += '' + row[column.property] + '	';
					});
					rowHTML += '
';
				});

				if (!rowHTML) rowHTML = self.placeholderRowHTML('0 ' + self.options.itemsText);

				self.$tbody.html(rowHTML);
				self.stretchHeight();

				self.$element.trigger('loaded');
			});

		},

		placeholderRowHTML: function (content) {
			return '
' + content + '
';
		},

		headerClicked: function (e) {
			var $target = $(e.target);
			if (!$target.hasClass('sortable')) return;

			var direction = this.options.dataOptions.sortDirection;
			var sort = this.options.dataOptions.sortProperty;
			var property = $target.data('property');

			if (sort === property) {
				this.options.dataOptions.sortDirection = (direction === 'asc') ? 'desc' : 'asc';
			} else {
				this.options.dataOptions.sortDirection = 'asc';
				this.options.dataOptions.sortProperty = property;
			}

			this.options.dataOptions.pageIndex = 0;
			this.updateColumns($target, this.options.dataOptions.sortDirection);
			this.renderData();
		},

		pagesizeChanged: function (e, pageSize) {
			if(pageSize) {
				this.options.dataOptions.pageSize = parseInt(pageSize.value, 10);
			} else {
				this.options.dataOptions.pageSize = parseInt($(e.target).val(), 10);
			}

			this.options.dataOptions.pageIndex = 0;
			this.renderData();
		},

		pageChanged: function (e) {
			var pageRequested = parseInt($(e.target).val(), 10);
			pageRequested = (isNaN(pageRequested)) ? 1 : pageRequested;
			var maxPages = this.$pageslabel.text();

			this.options.dataOptions.pageIndex =
				(pageRequested > maxPages) ? maxPages - 1 : pageRequested - 1;

			this.renderData();
		},

		searchChanged: function (e, search) {
			this.options.dataOptions.search = search;
			this.options.dataOptions.pageIndex = 0;
			this.renderData();
		},

		filterChanged: function (e, filter) {
			this.options.dataOptions.filter = filter;
			this.renderData();
		},

		previous: function () {
			this.options.dataOptions.pageIndex--;
			this.renderData();
		},

		next: function () {
			this.options.dataOptions.pageIndex++;
			this.renderData();
		},

		reload: function () {
			this.options.dataOptions.pageIndex = 0;
			this.renderData();
		},

		initStretchHeight: function () {
			this.$gridContainer = this.$element.parent();

			this.$element.wrap('
');
			this.$stretchWrapper = this.$element.parent();

			this.$headerTable = $('
').attr('class', this.$element.attr('class'));
			this.$footerTable = this.$headerTable.clone();

			this.$headerTable.prependTo(this.$gridContainer).addClass('datagrid-stretch-header');
			this.$thead.detach().appendTo(this.$headerTable);

			this.$sizingHeader = this.$thead.clone();
			this.$sizingHeader.find('tr:first').remove();

			this.$footerTable.appendTo(this.$gridContainer).addClass('datagrid-stretch-footer');
			this.$tfoot.detach().appendTo(this.$footerTable);
		},

		stretchHeight: function () {
			if (!this.$gridContainer) return;

			this.setColumnWidths();

			var targetHeight = this.$gridContainer.height();
			var headerHeight = this.$headerTable.outerHeight();
			var footerHeight = this.$footerTable.outerHeight();
			var overhead = headerHeight + footerHeight;

			this.$stretchWrapper.height(targetHeight - overhead);
		},

		setColumnWidths: function () {
			if (!this.$sizingHeader) return;

			this.$element.prepend(this.$sizingHeader);

			var $sizingCells = this.$sizingHeader.find('th');
			var columnCount = $sizingCells.length;

			function matchSizingCellWidth(i, el) {
				if (i === columnCount - 1) return;
				$(el).width($sizingCells.eq(i).width());
			}

			this.$colheader.find('th').each(matchSizingCellWidth);
			this.$tbody.find('tr:first > td').each(matchSizingCellWidth);

			this.$sizingHeader.detach();
		}
	};

	// DATAGRID PLUGIN DEFINITION

	$.fn.datagrid = function (option) {
		return this.each(function () {
			var $this = $(this);
			var data = $this.data('datagrid');
			var options = typeof option === 'object' && option;

			if (!data) $this.data('datagrid', (data = new Datagrid(this, options)));
			if (typeof option === 'string') data[option]();
		});
	};

	$.fn.datagrid.defaults = {
		dataOptions: { pageIndex: 0, pageSize: 10 },
		loadingHTML: '

',
		itemsText: 'items',
		itemText: 'item'
	};

	$.fn.datagrid.Constructor = Datagrid;

});
<|endoftext|>