<commit_before>"use strict";
var MapController = function($scope, $location, $timeout, analytics, config, mapTooltipFactory, navigation) {

	var MIN_TIME_BETWEEN_UPDATES = 100;

	var defaultMarker = null;
	var hoverMarker = null;
	var lastMarkerUpdateTime = null;
	var socket = null;

	$scope.mapOptions = null;
	$scope.markers = [];
	$scope.activeMarker = null;

	var doDeactivateMarker = function(marker) {
		var tooltip = marker.tooltips.pop();
		if (tooltip) {
			tooltip.destroy();
		}
		if (marker.tooltips.length === 0) {
			marker.setIcon(defaultMarker);
		}
	};

	var doActivateMarker = function(marker) {
		if ($scope.activeMarker && $scope.activeMarker !== marker) {
			doDeactivateMarker($scope.activeMarker);
			$scope.activeMarker = null;
		}
		var tooltip = mapTooltipFactory.create(marker);
		marker.tooltips.push(tooltip);
		marker.setIcon(hoverMarker);
		$scope.activeMarker = marker;
	};

	$scope.markerActivate = function(marker) {
		doActivateMarker(marker);
	};

	$scope.markerDeactivate = function(marker) {
		$timeout(function() {
			doDeactivateMarker(marker);
		}, 300);
	};

	$scope.markerClicked = function(marker) {
		if (Modernizr.touch && marker !== $scope.activeMarker) {
			doActivateMarker(marker);
			// Link to the entry will be on the tooltip itself.
		} else {
			navigation.toEntry(marker.hikeData.string_id);
		}
	};

	$scope.updateMarkers = function(event) {
		if (lastMarkerUpdateTime &&
				event.type !== "map-idle" &&
				event.timestamp - lastMarkerUpdateTime < MIN_TIME_BETWEEN_UPDATES) {
			// Recently updated, ignore this update request
			return;
		}

		lastMarkerUpdateTime = event.timestamp;
		var mapBounds = $scope.map.getBounds();
		var northEast = mapBounds.getNorthEast();
		var northEastLatLng = {
			latitude: northEast.lat(),
			longitude: northEast.lng()
		};
		var southWest = mapBounds.getSouthWest();
		var southWestLatLng = {
			latitude: southWest.lat(),
			longitude: southWest.lng()
		};
		socket.emit("get-hikes-in-bounds", { ne: northEastLatLng, sw: southWestLatLng });
	};

	var initIcons = function() {
		defaultMarker = {
			path: google.maps.SymbolPath.CIRCLE,
			fillOpacity: 1,
			fillColor: "#FF6262",
			strokeWeight: 1.0,
			scale: 4
		};
		hoverMarker = {
			path: google.maps.SymbolPath.CIRCLE,
			fillOpacity: 1,
			fillColor: "#FFFF33",
			strokeWeight: 1.0,
			scale: 5
		};
	};

	var initMapOptions = function() {
		// Default to a central view of the US
		var centerLatLng = new google.maps.LatLng(39.833333, -98.583333);
		var zoomLevel = 4;

		// If the url parameters lat / lng are set, zoom in there
		var urlParams = $location.search();
		var latString = urlParams.lat;
		var lngString = urlParams.lng;
		if ($.isNumeric(latString) && $.isNumeric(lngString)) {
			var lat = Number(latString);
			var lng = Number(lngString);
			centerLatLng = new google.maps.LatLng(lat, lng);
			zoomLevel = 12;
		}

		// else if we can guess the user's location, zoom into this location
		else if (google.loader.ClientLocation) {
			var clientLocation = google.loader.ClientLocation;
			centerLatLng = new google.maps.LatLng(clientLocation.latitude, clientLocation.longitude);
			zoomLevel = 7;
		}

		$scope.mapOptions = {
			zoom: zoomLevel,
			center: centerLatLng,
			mapTypeId: google.maps.MapTypeId.TERRAIN
		};
	};

	var initSocketIo = function() {
		socket = io.connect(config.socketIoPath);
		socket.on("get-hikes-in-bounds", function (data) {
			mergeMarkers(data);
		});
	};

	var compareLatLng = function(a, b) {
		if		(a.lat() < b.lat())		{	return -1;	}
		else if (a.lat() > b.lat())		{	return 1;	}
		else if (a.lng() < b.lng())		{	return -1;	}
		else if (a.lng() > b.lng())		{	return 1;	}
		else							{	return 0;	}
	};

	var mergeMarkers = function(newMarkers) {
		var i = 0;
		var j = 0;
		var mergedMarkers = [];
		while (i !== newMarkers.length || j !== $scope.markers.length) {
			var newLatLng = null;
			var oldMarker = null;
			var oldLatLng = null;

			if (i < newMarkers.length) {
				newLatLng = new google.maps.LatLng(newMarkers[i].latitude, newMarkers[i].longitude);
			}

			if (j < $scope.markers.length) {
				oldMarker = $scope.markers[j];
				oldLatLng = oldMarker.getPosition();
			}

			if (!oldLatLng || (newLatLng && compareLatLng(newLatLng, oldLatLng) < 0)) {
				// This is a brand new marker, add it
				var marker = new google.maps.Marker({
					icon: defaultMarker,
					map: $scope.map,
					position: newLatLng,
					hikeData: newMarkers[i],
					tooltips: []
				});
				mergedMarkers.push(marker);
				i++;
			} else if (!newLatLng || (oldLatLng && compareLatLng(newLatLng, oldLatLng) > 0)) {
				// The old marker is no longer in use, remove it
				oldMarker.setMap(null);
				j++;
			} else {
				// Use the existing marker
				mergedMarkers.push(oldMarker);
				i++;
				j++;
			}
		}
		$scope.markers = mergedMarkers;
	};

	// Init
	initIcons();
	initMapOptions();
	initSocketIo();
	$scope.htmlReady();
};

MapController.$inject = ["$scope", "$location", "$timeout", "analytics", "config", "mapTooltipFactory", "navigation"];<commit_msg>Try using consistent orange accent color for map markers.<commit_after>"use strict";
var MapController = function($scope, $location, $timeout, analytics, config, mapTooltipFactory, navigation) {

	var MIN_TIME_BETWEEN_UPDATES = 100;

	var defaultMarker = null;
	var hoverMarker = null;
	var lastMarkerUpdateTime = null;
	var socket = null;

	$scope.mapOptions = null;
	$scope.markers = [];
	$scope.activeMarker = null;

	var doDeactivateMarker = function(marker) {
		var tooltip = marker.tooltips.pop();
		if (tooltip) {
			tooltip.destroy();
		}
		if (marker.tooltips.length === 0) {
			marker.setIcon(defaultMarker);
		}
	};

	var doActivateMarker = function(marker) {
		if ($scope.activeMarker && $scope.activeMarker !== marker) {
			doDeactivateMarker($scope.activeMarker);
			$scope.activeMarker = null;
		}
		var tooltip = mapTooltipFactory.create(marker);
		marker.tooltips.push(tooltip);
		marker.setIcon(hoverMarker);
		$scope.activeMarker = marker;
	};

	$scope.markerActivate = function(marker) {
		doActivateMarker(marker);
	};

	$scope.markerDeactivate = function(marker) {
		$timeout(function() {
			doDeactivateMarker(marker);
		}, 300);
	};

	$scope.markerClicked = function(marker) {
		if (Modernizr.touch && marker !== $scope.activeMarker) {
			doActivateMarker(marker);
			// Link to the entry will be on the tooltip itself.
		} else {
			navigation.toEntry(marker.hikeData.string_id);
		}
	};

	$scope.updateMarkers = function(event) {
		if (lastMarkerUpdateTime &&
				event.type !== "map-idle" &&
				event.timestamp - lastMarkerUpdateTime < MIN_TIME_BETWEEN_UPDATES) {
			// Recently updated, ignore this update request
			return;
		}

		lastMarkerUpdateTime = event.timestamp;
		var mapBounds = $scope.map.getBounds();
		var northEast = mapBounds.getNorthEast();
		var northEastLatLng = {
			latitude: northEast.lat(),
			longitude: northEast.lng()
		};
		var southWest = mapBounds.getSouthWest();
		var southWestLatLng = {
			latitude: southWest.lat(),
			longitude: southWest.lng()
		};
		socket.emit("get-hikes-in-bounds", { ne: northEastLatLng, sw: southWestLatLng });
	};

	var initIcons = function() {
		defaultMarker = {
			path: google.maps.SymbolPath.CIRCLE,
			fillOpacity: 1,
			fillColor: "#EB593C",
			strokeWeight: 1.0,
			scale: 4
		};
		hoverMarker = {
			path: google.maps.SymbolPath.CIRCLE,
			fillOpacity: 1,
			fillColor: "#FFFF33",
			strokeWeight: 1.0,
			scale: 5
		};
	};

	var initMapOptions = function() {
		// Default to a central view of the US
		var centerLatLng = new google.maps.LatLng(39.833333, -98.583333);
		var zoomLevel = 4;

		// If the url parameters lat / lng are set, zoom in there
		var urlParams = $location.search();
		var latString = urlParams.lat;
		var lngString = urlParams.lng;
		if ($.isNumeric(latString) && $.isNumeric(lngString)) {
			var lat = Number(latString);
			var lng = Number(lngString);
			centerLatLng = new google.maps.LatLng(lat, lng);
			zoomLevel = 12;
		}

		// else if we can guess the user's location, zoom into this location
		else if (google.loader.ClientLocation) {
			var clientLocation = google.loader.ClientLocation;
			centerLatLng = new google.maps.LatLng(clientLocation.latitude, clientLocation.longitude);
			zoomLevel = 7;
		}

		$scope.mapOptions = {
			zoom: zoomLevel,
			center: centerLatLng,
			mapTypeId: google.maps.MapTypeId.TERRAIN
		};
	};

	var initSocketIo = function() {
		socket = io.connect(config.socketIoPath);
		socket.on("get-hikes-in-bounds", function (data) {
			mergeMarkers(data);
		});
	};

	var compareLatLng = function(a, b) {
		if		(a.lat() < b.lat())		{	return -1;	}
		else if (a.lat() > b.lat())		{	return 1;	}
		else if (a.lng() < b.lng())		{	return -1;	}
		else if (a.lng() > b.lng())		{	return 1;	}
		else							{	return 0;	}
	};

	var mergeMarkers = function(newMarkers) {
		var i = 0;
		var j = 0;
		var mergedMarkers = [];
		while (i !== newMarkers.length || j !== $scope.markers.length) {
			var newLatLng = null;
			var oldMarker = null;
			var oldLatLng = null;

			if (i < newMarkers.length) {
				newLatLng = new google.maps.LatLng(newMarkers[i].latitude, newMarkers[i].longitude);
			}

			if (j < $scope.markers.length) {
				oldMarker = $scope.markers[j];
				oldLatLng = oldMarker.getPosition();
			}

			if (!oldLatLng || (newLatLng && compareLatLng(newLatLng, oldLatLng) < 0)) {
				// This is a brand new marker, add it
				var marker = new google.maps.Marker({
					icon: defaultMarker,
					map: $scope.map,
					position: newLatLng,
					hikeData: newMarkers[i],
					tooltips: []
				});
				mergedMarkers.push(marker);
				i++;
			} else if (!newLatLng || (oldLatLng && compareLatLng(newLatLng, oldLatLng) > 0)) {
				// The old marker is no longer in use, remove it
				oldMarker.setMap(null);
				j++;
			} else {
				// Use the existing marker
				mergedMarkers.push(oldMarker);
				i++;
				j++;
			}
		}
		$scope.markers = mergedMarkers;
	};

	// Init
	initIcons();
	initMapOptions();
	initSocketIo();
	$scope.htmlReady();
};

MapController.$inject = ["$scope", "$location", "$timeout", "analytics", "config", "mapTooltipFactory", "navigation"];<|endoftext|>


<commit_before>import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import queryString from 'query-string';
import { FormattedMessage } from 'react-intl';
import Badge from '@folio/stripes-components/lib/Badge';
import Button from '@folio/stripes-components/lib/Button';
import { Accordion } from '@folio/stripes-components/lib/Accordion';
import Icon from '@folio/stripes-components/lib/Icon';
import List from '@folio/stripes-components/lib/List';

/**
 * User-details "Accounts" accordian pane.
 *
 * Show links to the open- and closed-accounts in the body; include the
 * number of open-accounts in the preview.
 */
class UserAccounts extends React.Component {
  static manifest = Object.freeze({
    accountsHistory: {
      type: 'okapi',
      records: 'accounts',
      GET: {
        path: 'accounts?query=(userId=:{id})&limit=100',
      },
    },
    openAccountsCount: {
      type: 'okapi',
      GET: {
        path: 'accounts?query=(userId=:{id} and status.name<>Closed)&limit=%{activeRecord.limit}',
      },
    },
    closedAccountsCount: {
      type: 'okapi',
      GET: {
        path: 'accounts?query=(userId=:{id} and status.name=Closed)&limit=%{activeRecord.limit}',
      },
    },
    activeRecord: {},
    userid: {},
  });

  static propTypes = {
    resources: PropTypes.shape({
      accountsHistory: PropTypes.shape({
        records: PropTypes.arrayOf(PropTypes.object),
      }),
    }),
    mutator: PropTypes.shape({
      activeRecord: PropTypes.object,
    }),
    onClickViewChargeFeeFine: PropTypes.func.isRequired,
    accordionId: PropTypes.string,
    addRecord: PropTypes.bool,
    expanded: PropTypes.bool,
    onToggle: PropTypes.func,
    location: PropTypes.shape({
      search: PropTypes.string,
      pathname: PropTypes.string,
    }),
  };

  componentDidMount() {
    this.props.mutator.activeRecord.update({ limit: 1 });
  }

  componentDidUpdate(prevProps) {
    if (this.props.addRecord !== prevProps.addRecord) {
      if (!prevProps.addRecord) this.props.mutator.activeRecord.update({ limit: 2 });
      else this.props.mutator.activeRecord.update({ limit: 1 });
    }
  }

  render() {
    const resources = this.props.resources;
    const openAccountsTotal = _.get(resources.openAccountsCount, ['records', '0', 'totalRecords'], 0);
    const closedAccountsTotal = _.get(resources.closedAccountsCount, ['records', '0', 'totalRecords'], 0);
    const { expanded, onToggle, accordionId } = this.props;

    const openAccountsCount = (_.get(resources.openAccountsCount, ['isPending'], true)) ? -1 : openAccountsTotal;
    const closedAccountsCount = (_.get(resources.closedAccountsCount, ['isPending'], true)) ? -1 : closedAccountsTotal;

    const query = this.props.location.search ? queryString.parse(this.props.location.search) : {};

    const accountsLoaded = openAccountsCount >= 0 && closedAccountsCount >= 0;
    const displayWhenClosed = accountsLoaded ? (<Badge>{openAccountsCount}</Badge>) : (<Icon icon="spinner-ellipsis" width="10px" />);
    const displayWhenOpen = (<Button onClick={e => this.props.onClickViewChargeFeeFine(e, {})}><FormattedMessage id="ui-users.accounts.chargeManual" /></Button>);
    return (
      <Accordion
        open={expanded}
        id={accordionId}
        onToggle={onToggle}
        label={<FormattedMessage id="ui-users.accounts.title" />}
        displayWhenClosed={displayWhenClosed}
        displayWhenOpen={displayWhenOpen}
      >
        {accountsLoaded ?
          <List
            listStyle="bullets"
            itemFormatter={(item, index) => (

                <Link
                  id={item.id}
                  to={`${this.props.location.pathname}?${queryString.stringify({ ...query, layer: item.layer })}`}
                >
                  <FormattedMessage id={item.formattedMessageId} values={{ count: item.count }} />
                </Link>

)}
            items={[
              {
                id: 'clickable-viewcurrentaccounts',
                count: openAccountsCount,
                formattedMessageId: 'ui-users.accounts.numOpenAccounts',
                layer: 'open-accounts',
              },
              {
                id: 'clickable-viewclosedaccounts',
                count: closedAccountsCount,
                formattedMessageId: 'ui-users.accounts.numClosedAccounts',
                layer: 'closed-accounts',
              },
            ]}
          /> : <Icon icon="spinner-ellipsis" width="10px" />
        }
      </Accordion>
    );
  }
}

export default UserAccounts;
<commit_msg>Modify counter<commit_after>import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import queryString from 'query-string';
import { FormattedMessage } from 'react-intl';
import Badge from '@folio/stripes-components/lib/Badge';
import Button from '@folio/stripes-components/lib/Button';
import { Accordion } from '@folio/stripes-components/lib/Accordion';
import Icon from '@folio/stripes-components/lib/Icon';
import List from '@folio/stripes-components/lib/List';

/**
 * User-details "Accounts" accordian pane.
 *
 * Show links to the open- and closed-accounts in the body; include the
 * number of open-accounts in the preview.
 */
class UserAccounts extends React.Component {
  static manifest = Object.freeze({
    accountsHistory: {
      type: 'okapi',
      records: 'accounts',
      GET: {
        path: 'accounts?query=(userId=:{id})&limit=100',
      },
    },
    openAccountsCount: {
      type: 'okapi',
      accumulate: 'true',
      GET: {
        path: 'accounts?query=(userId=:{id} and status.name<>Closed)&limit=1',
      },
    },
    closedAccountsCount: {
      type: 'okapi',
      accumulate: 'true',
      GET: {
        path: 'accounts?query=(userId=:{id} and status.name=Closed)&limit=1',
      },
    },
    activeRecord: {},
    userid: {},
  });

  static propTypes = {
    resources: PropTypes.shape({
      accountsHistory: PropTypes.shape({
        records: PropTypes.arrayOf(PropTypes.object),
      }),
    }),
    mutator: PropTypes.shape({
      openAccountsCount: PropTypes.object,
      closedAccountsCount: PropTypes.object,
    }),
    onClickViewChargeFeeFine: PropTypes.func.isRequired,
    accordionId: PropTypes.string,
    addRecord: PropTypes.bool,
    expanded: PropTypes.bool,
    onToggle: PropTypes.func,
    location: PropTypes.shape({
      search: PropTypes.string,
      pathname: PropTypes.string,
    }),
  };

  constructor(props) {
    super(props);
    this.state = {
      openAccounts: 0,
      closedAccounts: 0,
    };
  }

  componentDidMount() {
    this.props.mutator.openAccountsCount.GET().then(records => {
      this.setState({
        openAccounts: records.totalRecords,
      });
    });
    this.props.mutator.closedAccountsCount.GET().then(records => {
      this.setState({
        closedAccounts: records.totalRecords,
      });
    });
  }

  componentDidUpdate(prevProps) {
    if (this.props.addRecord !== prevProps.addRecord) {
      this.props.mutator.openAccountsCount.GET().then(records => {
        this.setState({
          openAccounts: records.totalRecords,
        });
      });
      this.props.mutator.closedAccountsCount.GET().then(records => {
        this.setState({
          closedAccounts: records.totalRecords,
        });
      });
    }
  }

  render() {
    const resources = this.props.resources;
    const openAccountsTotal = this.state.openAccounts || 0;
    const closedAccountsTotal = this.state.closedAccounts || 0;
    const { expanded, onToggle, accordionId } = this.props;

    const openAccountsCount = (_.get(resources.openAccountsCount, ['isPending'], true)) ? -1 : openAccountsTotal;
    const closedAccountsCount = (_.get(resources.closedAccountsCount, ['isPending'], true)) ? -1 : closedAccountsTotal;

    const query = this.props.location.search ? queryString.parse(this.props.location.search) : {};

    const accountsLoaded = openAccountsCount >= 0 && closedAccountsCount >= 0;
    const displayWhenClosed = accountsLoaded ? (<Badge>{openAccountsCount}</Badge>) : (<Icon icon="spinner-ellipsis" width="10px" />);
    const displayWhenOpen = (<Button onClick={e => this.props.onClickViewChargeFeeFine(e, {})}><FormattedMessage id="ui-users.accounts.chargeManual" /></Button>);
    return (
      <Accordion
        open={expanded}
        id={accordionId}
        onToggle={onToggle}
        label={<FormattedMessage id="ui-users.accounts.title" />}
        displayWhenClosed={displayWhenClosed}
        displayWhenOpen={displayWhenOpen}
      >
        {accountsLoaded ?
          <List
            listStyle="bullets"
            itemFormatter={(item, index) => (

                <Link
                  id={item.id}
                  to={`${this.props.location.pathname}?${queryString.stringify({ ...query, layer: item.layer })}`}
                >
                  <FormattedMessage id={item.formattedMessageId} values={{ count: item.count }} />
                </Link>

)}
            items={[
              {
                id: 'clickable-viewcurrentaccounts',
                count: openAccountsCount,
                formattedMessageId: 'ui-users.accounts.numOpenAccounts',
                layer: 'open-accounts',
              },
              {
                id: 'clickable-viewclosedaccounts',
                count: closedAccountsCount,
                formattedMessageId: 'ui-users.accounts.numClosedAccounts',
                layer: 'closed-accounts',
              },
            ]}
          /> : <Icon icon="spinner-ellipsis" width="10px" />
        }
      </Accordion>
    );
  }
}

export default UserAccounts;
<|endoftext|>


<commit_before>
'use strict';

const http = require('http');
const Koa = require('..');
const app = new Koa();

// number of middleware

let n = parseInt(process.env.MW || '1', 10);
console.log(`  ${n} middleware`);

while (n--) {
  app.use(function *(next){
    yield *next;
  });
}

const body = new Buffer('Hello World');

app.use(function *(next){
  yield *next;
  this.body = body;
});

app.listen(3333);
<commit_msg>update bench case<commit_after>
'use strict';

const http = require('http');
const Koa = require('..');
const app = new Koa();

// number of middleware

let n = parseInt(process.env.MW || '1', 10);
console.log(`  ${n} middleware`);

while (n--) {
  app.use(function *(ctx, next){
    yield next();
  });
}

const body = new Buffer('Hello World');

app.use(function *(ctx, next){
  yield next();
  this.body = body;
});

app.listen(3333);
<|endoftext|>


<commit_before>// On the shoulders of giants:
// --------------------------
// MDN - Service Worker API - Using Service Workers
// https://serviceworke.rs
// https://github.com/GoogleChrome/samples/tree/gh-pages/service-worker

'use strict';

import * as CACHE from './swCache.json';

const debug = require('debug')('am:sw');

const CACHE_VERSION = 10;
const CURRENT_CACHES = {
  font: `font-cache-v${CACHE_VERSION}`,
  css: `css-cache-v${CACHE_VERSION}`,
  html: `html-cache-v${CACHE_VERSION}`,
  img: `img-cache-v${CACHE_VERSION}`,
  js: `js-cache-v${CACHE_VERSION}`
  // json: `json-cache-v${CACHE_VERSION}` // this should be in indexedDB.
  // Currently appcache has everything stored in it, including json.
  // Shouldn't be a problem to move everything to IDB being that it has incredibly high compatibility (~94%).
  // Need to move all non-structural files to be stored in and accessed from IDB.
  // This includes all JSON, non-base images, and files used by various challenges.
  // IDB currently just stores the user's progress.
};

// challenges currently do not need to be imported as it's part of the main.bundle.js

self.addEventListener('install', event => {
  self.skipWaiting(); // make queued SW active.
  debug('in the install event');
  event.waitUntil(
    Object.keys(CURRENT_CACHES).forEach(key => {
      caches
        .open(CURRENT_CACHES[key])
        .then(cache => cache.addAll(CACHE[key]));
        /*
        .then(() =>
          self.skipWaiting() // make queued SW active.
        );
       */
    })
  );
});

self.addEventListener('fetch', event => {
  debug(`Handling fetch event for ${event.request.url}`);

  let requestType;

  switch (event.request.url.split('.')[event.request.url.split('.').length - 1]) {
    case 'http://localhost:3000/':
    case 'http://localhost:8080/':
    case 'com/':
    case 'io/arcade-mode/':
      requestType = 'html';
      break;
    case 'ttf':
      requestType = 'font';
      break;
    case 'css':
      requestType = 'css';
      break;
    case 'svg':
    case 'png':
    case 'jpeg':
    case 'jpg':
      requestType = 'img';
      break;
    case 'js':
      requestType = 'js';
      break;
    default:
      requestType = 'not found';
  }

  if (requestType === 'not found') {
    debug(`Requested type, ${event.request.url}, is not in the cache`);
    debug(event.request.url.split('.')[event.request.url.split('.').length - 1]);
    return fetch(event.request);
  }
  event.respondWith(
    fromNetwork(event.request, 400).catch(() =>
      fromCache(event.request, CURRENT_CACHES[requestType]))
  /*
    caches.open(CURRENT_CACHES[requestType]).then(cache =>
      cache.match(event.request).then(res => {
        if (res) {
          debug(`Response found in cache ${res}.`);
          return res;
        }
        return fetch(event.request);
      })
    )
  */
  );
});

self.addEventListener('activate', event => {
  debug('[activate]: Activating Service Worker.');

  self.clients.claim();

  // Delete all caches that aren't named in CURRENT_CACHES
  const expectedCacheNames = Object.keys(CURRENT_CACHES).map(key => CURRENT_CACHES[key]);

  event.waitUntil(
    caches.keys().then(cacheNames =>
      Promise.all(cacheNames.map(cacheName => {
        if (expectedCacheNames.indexOf(cacheName) === -1) {
          debug(`Deleting out of date cache: ${cacheName}`);
          return caches.delete(cacheName);
        }
      }))
    )
    /*
    .then(() => self.clients.claim())
   */
  );
});

function fromCache (__request__, __CACHE__) {
  return caches.open(__CACHE__).then(cache =>
    cache.match(__request__).then(res => {
      if (res) {
        debug(`Response found in cache ${res}.`);
        return res;
      }
      return fetch(__request__);
    }));
}

function fromNetwork (__request__, __timeout__) {
  return new Promise((fulfill, reject) => {
    const timeoutId = setTimeout(reject, __timeout__);

    fetch(__request__).then(res => {
      debug(`Response found from network ${res}.`);
      clearTimeout(timeoutId);
      fulfill(res);
    }, reject);
  });
}

<commit_msg>cache bust<commit_after>// On the shoulders of giants:
// --------------------------
// MDN - Service Worker API - Using Service Workers
// https://serviceworke.rs
// https://github.com/GoogleChrome/samples/tree/gh-pages/service-worker

'use strict';

import * as CACHE from './swCache.json';

const debug = require('debug')('am:sw');

const CACHE_VERSION = 11;
const CURRENT_CACHES = {
  font: `font-cache-v${CACHE_VERSION}`,
  css: `css-cache-v${CACHE_VERSION}`,
  html: `html-cache-v${CACHE_VERSION}`,
  img: `img-cache-v${CACHE_VERSION}`,
  js: `js-cache-v${CACHE_VERSION}`
  // json: `json-cache-v${CACHE_VERSION}` // this should be in indexedDB.
  // Currently appcache has everything stored in it, including json.
  // Shouldn't be a problem to move everything to IDB being that it has incredibly high compatibility (~94%).
  // Need to move all non-structural files to be stored in and accessed from IDB.
  // This includes all JSON, non-base images, and files used by various challenges.
  // IDB currently just stores the user's progress.
};

// challenges currently do not need to be imported as it's part of the main.bundle.js

self.addEventListener('install', event => {
  self.skipWaiting(); // make queued SW active.
  debug('in the install event');
  event.waitUntil(
    Object.keys(CURRENT_CACHES).forEach(key => {
      caches
        .open(CURRENT_CACHES[key])
        .then(cache => cache.addAll(CACHE[key]));
        /*
        .then(() =>
          self.skipWaiting() // make queued SW active.
        );
       */
    })
  );
});

self.addEventListener('fetch', event => {
  debug(`Handling fetch event for ${event.request.url}`);

  let requestType;

  switch (event.request.url.split('.')[event.request.url.split('.').length - 1]) {
    case 'http://localhost:3000/':
    case 'http://localhost:8080/':
    case 'com/':
    case 'io/arcade-mode/':
      requestType = 'html';
      break;
    case 'ttf':
      requestType = 'font';
      break;
    case 'css':
      requestType = 'css';
      break;
    case 'svg':
    case 'png':
    case 'jpeg':
    case 'jpg':
      requestType = 'img';
      break;
    case 'js':
      requestType = 'js';
      break;
    default:
      requestType = 'not found';
  }

  if (requestType === 'not found') {
    debug(`Requested type, ${event.request.url}, is not in the cache`);
    debug(event.request.url.split('.')[event.request.url.split('.').length - 1]);
    return fetch(event.request);
  }
  event.respondWith(
    fromNetwork(event.request, 400).catch(() =>
      fromCache(event.request, CURRENT_CACHES[requestType]))
  /*
    caches.open(CURRENT_CACHES[requestType]).then(cache =>
      cache.match(event.request).then(res => {
        if (res) {
          debug(`Response found in cache ${res}.`);
          return res;
        }
        return fetch(event.request);
      })
    )
  */
  );
});

self.addEventListener('activate', event => {
  debug('[activate]: Activating Service Worker.');

  self.clients.claim();

  // Delete all caches that aren't named in CURRENT_CACHES
  const expectedCacheNames = Object.keys(CURRENT_CACHES).map(key => CURRENT_CACHES[key]);

  event.waitUntil(
    caches.keys().then(cacheNames =>
      Promise.all(cacheNames.map(cacheName => {
        if (expectedCacheNames.indexOf(cacheName) === -1) {
          debug(`Deleting out of date cache: ${cacheName}`);
          return caches.delete(cacheName);
        }
      }))
    )
    /*
    .then(() => self.clients.claim())
   */
  );
});

function fromCache (__request__, __CACHE__) {
  return caches.open(__CACHE__).then(cache =>
    cache.match(__request__).then(res => {
      if (res) {
        debug(`Response found in cache ${res}.`);
        return res;
      }
      return fetch(__request__);
    }));
}

function fromNetwork (__request__, __timeout__) {
  return new Promise((fulfill, reject) => {
    const timeoutId = setTimeout(reject, __timeout__);

    fetch(__request__).then(res => {
      debug(`Response found from network ${res}.`);
      clearTimeout(timeoutId);
      fulfill(res);
    }, reject);
  });
}

<|endoftext|>


<commit_before>import React from 'react'

import { Container, Background, Content } from './styled'

const Home = () => (
  <Container id="home">
    <Background />

    <Content>

        Somos la comunidad de{' '}

          freeCodeCamp
        {' '}
        en{' '}

          Buenos Aires

Veni a conocernos para aprender sobre desarrollo web en comunidad!

      <video preload="preload" controls="controls" autoplay="autoplay" loop="loop">
        <source src="https://res.cloudinary.com/dnbygvvyz/video/upload/v1552479645/video-fcc.mp4" />
      </video>
    </Content>
  </Container>
)

export default Home
<commit_msg>Fix for video<commit_after>import React from 'react'

import { Container, Background, Content } from './styled'

const Home = () => (
  <Container id="home">
    <Background />

    <Content>

        Somos la comunidad de{' '}

          freeCodeCamp
        {' '}
        en{' '}

          Buenos Aires

Veni a conocernos para aprender sobre desarrollo web en comunidad!

      <video controls="controls" autoplay="autoplay" loop="loop">
        <source src="https://res.cloudinary.com/dnbygvvyz/video/upload/v1552479645/video-fcc.mp4" />
      </video>
    </Content>
  </Container>
)

export default Home
<|endoftext|>


<commit_before>// Libraries
import React, { PureComponent, PropTypes } from 'react'

// React Components
import NavbarButton from 'pages/shared/NavbarButton'
import { Link } from 'react-router'

// Local styling
import './index.css'

export default class Navbar extends PureComponent {

  constructor () {
    super()

    // set initial state
    this.state = {
      pressedNavButton: ''
    }
  }

  componentDidMount () {
    // create listeners, to enable scroll transition
    window.addEventListener('scroll', function () {
      document.body.classList[window.scrollY > 35 ? 'add' : 'remove']('scrolled')
    })
    window.addEventListener('scroll', function () {
      document.body.classList[window.scrollY < 35 ? 'add' : 'remove']('not-scrolled')
    })
  }

  componentWillReceiveProps (nextProps) {
    // handle marking of navbarbutton
    this.markNavButton(nextProps.location.pathname)
  }

  // sets button as pressed, when called
  markNavButton (title) {
    this.setState({
      pressedNavButton: title
    })
  }

  render () {
    return (

          {/* <button className='hamburger_menu'>

          </button> */}

              <Link to='/'>
Acuity
</Link>

              <NavbarButton
                to='/'
                title='Home'
                pressedButton={this.state.pressedNavButton}
              />
              <NavbarButton
                to='/login'
                title='Login'
                type='extern'
                pressedButton={this.state.pressedNavButton}
              />
              <NavbarButton
                to='/register'
                title='Register'
                type='extern'
                pressedButton={this.state.pressedNavButton}
              />
              <NavbarButton
                to='/browse'
                title='Browse'
                pressedButton={this.state.pressedNavButton}
              />
              <NavbarButton
                to='/upload'
                title='Upload'
                type='intern'
                pressedButton={this.state.pressedNavButton}
              />
              <NavbarButton
                to='/logout'
                title='Logout'
                type='intern'
                pressedButton={this.state.pressedNavButton}
              />

          {this.props.children}

    )
  }
}

Navbar.propTypes = {
  location: PropTypes.object.isRequired,
  children: PropTypes.object.isRequired
}
<commit_msg>Edit 'Browse' button placement in navbar<commit_after>// Libraries
import React, { PureComponent, PropTypes } from 'react'

// React Components
import NavbarButton from 'pages/shared/NavbarButton'
import { Link } from 'react-router'

// Local styling
import './index.css'

export default class Navbar extends PureComponent {

  constructor () {
    super()

    // set initial state
    this.state = {
      pressedNavButton: ''
    }
  }

  componentDidMount () {
    // create listeners, to enable scroll transition
    window.addEventListener('scroll', function () {
      document.body.classList[window.scrollY > 35 ? 'add' : 'remove']('scrolled')
    })
    window.addEventListener('scroll', function () {
      document.body.classList[window.scrollY < 35 ? 'add' : 'remove']('not-scrolled')
    })
  }

  componentWillReceiveProps (nextProps) {
    // handle marking of navbarbutton
    this.markNavButton(nextProps.location.pathname)
  }

  // sets button as pressed, when called
  markNavButton (title) {
    this.setState({
      pressedNavButton: title
    })
  }

  render () {
    return (

          {/* <button className='hamburger_menu'>

          </button> */}

              <Link to='/'>
Acuity
</Link>

              <NavbarButton
                to='/'
                title='Home'
                pressedButton={this.state.pressedNavButton}
              />
              <NavbarButton
                to='/browse'
                title='Browse'
                pressedButton={this.state.pressedNavButton}
              />
              <NavbarButton
                to='/login'
                title='Login'
                type='extern'
                pressedButton={this.state.pressedNavButton}
              />
              <NavbarButton
                to='/register'
                title='Register'
                type='extern'
                pressedButton={this.state.pressedNavButton}
              />
              <NavbarButton
                to='/upload'
                title='Upload'
                type='intern'
                pressedButton={this.state.pressedNavButton}
              />
              <NavbarButton
                to='/logout'
                title='Logout'
                type='intern'
                pressedButton={this.state.pressedNavButton}
              />

          {this.props.children}

    )
  }
}

Navbar.propTypes = {
  location: PropTypes.object.isRequired,
  children: PropTypes.object.isRequired
}
<|endoftext|>


<commit_before>// Process errors, if happens.
// AND make default answer (for http)
//

'use strict';

var http = require('http');
var _    = require('lodash');

////////////////////////////////////////////////////////////////////////////////

module.exports = function (N) {

  N.wire.after([ 'responder:http', 'responder:rpc' ], { priority: 50 }, function error_process(env) {
    var err = env.err;

    if (!err) {
      return;
    }

    // Extend sugared errors
    // Example: next(404);
    if (err === +err) {
      err = env.err = { code: env.err };
    }

    if (err.code) {
      env.status = err.code;

      env.headers = _.defaults(err.head || {}, {
        'Content-Type': 'text/plain; charset=utf-8'
      });

      if (err.data && typeof err.data === 'object') {
        env.body = JSON.stringify(err.data);
      } else {
        env.body = err.message || '[' + err.code + '] ' + http.STATUS_CODES[err.code];
      }

      if (!err.message) {
        // For convenience. This details will be send later via RPC
        err.message = http.STATUS_CODES[err.code];
      }

      return;
    }

    // Still no code -> we got Error object or string
    // Example: next(new Error('Fatal fuckup'))

    var e = {
      code: N.io.APP_ERROR
    };

    // Add message if required
    if (N.environment === 'development') {
      e.message = '[500] ' + (err.stack || err.message || err.toString());
    } else {
      e.message = _.isString(err) ? ('[500]' + err) : '[500] Internal Server Error';
    }

    env.err = e;

    env.status = e.code;
    env.headers = { 'Content-Type': 'text/plain; charset=utf-8' };
    env.body = e.message;
  });
};
<commit_msg>Fixed error handling<commit_after>// Process errors, if happens.
// AND make default answer (for http)
//

'use strict';

var http = require('http');
var _    = require('lodash');

////////////////////////////////////////////////////////////////////////////////

module.exports = function (N) {

  N.wire.after([ 'responder:http', 'responder:rpc' ], { priority: 50 }, function error_process(env) {
    var err = env.err;

    if (!err) {
      return;
    }

    // Special case for errors thrown by MongoDB: `code` field is not http status code.
    if (err.name && err.name === 'MongoError') {
      err._code = err.code;
      delete err.code;
    }

    // Extend sugared errors
    // Example: next(404);
    if (err === +err) {
      err = env.err = { code: env.err };
    }

    if (err.code) {
      env.status = err.code;

      env.headers = _.defaults(err.head || {}, {
        'Content-Type': 'text/plain; charset=utf-8'
      });

      if (err.data && typeof err.data === 'object') {
        env.body = JSON.stringify(err.data);
      } else {
        env.body = err.message || '[' + err.code + '] ' + http.STATUS_CODES[err.code];
      }

      if (!err.message) {
        // For convenience. This details will be send later via RPC
        err.message = http.STATUS_CODES[err.code];
      }

      return;
    }

    // Still no code -> we got Error object or string
    // Example: next(new Error('Fatal fuckup'))

    var e = {
      code: N.io.APP_ERROR
    };

    // Add message if required
    if (N.environment === 'development') {
      e.message = '[500] ' + (err.stack || err.message || err.toString());
    } else {
      e.message = _.isString(err) ? ('[500]' + err) : '[500] Internal Server Error';
    }

    env.err = e;

    env.status = e.code;
    env.headers = { 'Content-Type': 'text/plain; charset=utf-8' };
    env.body = e.message;
  });
};
<|endoftext|>


<commit_before>angular.module('cardcast.main', [])

//Set up main controller for Sender.
.controller('MainCtrl', function($scope, $timeout, $location, Service, user) {

  $scope.deck = {};
  $scope.currentCard = {};

  //toggles popup warning using 'ng-show' in main.html
  $scope.showWarning = false;
  $scope.showDelete = false;
  $scope.username = user;

//Loads all of the user's cards from DB into controller
  $scope.getDeck = function() {
    Service.getDeck()
      .then(function(resp) {
        $scope.deck = resp;
      });
  };

  //First checks for a session and sees if anyone else is currently casting.
  //Casts the card that invoked it as long as no one else is casting,
  //otherwise triggers the popup warning.
  $scope.showPopup = function(card) {

    //if there is an active session and no one is casting, cast the card
    if (session && !isCasting) {
      console.log(session.status);
      $scope.castCard(card);

    //if there is an active session and someone else is casting show popup
    } else if (session && isCasting) {
      $scope.showWarning = true;
      $scope.currentCard = card;
    } else if (chrome.cast) {

    //if there is no active session request one
      chrome.cast.requestSession(function(currentSession) {
        sessionListener.call(null, currentSession);

        //Provides extra time for the reciever to respond
        $timeout(function() {
          if (!isCasting) {
            $scope.castCard(card);
          } else {
            $scope.showWarning = true;
            $scope.currentCard = card;
          }
        }, 100);

      }, console.log.bind(null, 'onError: '));
    }
  };

  //clears popup warning if user cancels the cast
  $scope.cancelCast = function() {
    $scope.showWarning = false;
  };

  //Sends cast using the card that invoked showPopup. The username tracks who is currently casting
  //Passing the 'clear' parameter stops the current cast and reverts everything to default state.
  $scope.castCard = function(card, clear = false) {
    var message = {
      username: clear ? null : user,
      card: clear ? '
Welcome to CardCast!

Nothing has been casted yet...' : card.card,
      cardId: clear ? null : card._id
    };
    $scope.showWarning = false;
    session.sendMessage(namespace, JSON.stringify(message), console.log.bind(null,'onSuccess: ', 'Message was sent: ' + message), console.log.bind(null, 'onError: '));
  };

  //Deletes selected card from the database
  $scope.deleteCard = function(card) {
    Service.deleteCard(card)
      .then(function(resp) {
        Service.getDeck()
          .then(function(resp) {
            $scope.deck = resp;
            $scope.showDelete = false;
          });
      });
  };

  $scope.warnDelete = function(card) {
    $scope.showDelete = true;
    $scope.currentCard = card;
  };

  $scope.cancelDelete = function() {
    $scope.showDelete = false;
  };

  $scope.getDeck();
});
<commit_msg>Deleting unnecessary dependencies<commit_after>angular.module('cardcast.main', [])

//Set up main controller for Sender.
.controller('MainCtrl', function($scope, $timeout, $location, Service, user) {

  $scope.deck = {};
  $scope.currentCard = {};

  //toggles popup warning using 'ng-show' in main.html
  $scope.showWarning = false;
  $scope.showDelete = false;
  $scope.username = user;

//Loads all of the user's cards from DB into controller
  $scope.getDeck = function() {
    Service.getDeck()
      .then(function(resp) {
        $scope.deck = resp;
      });
  };

  //First checks for a session and sees if anyone else is currently casting.
  //Casts the card that invoked it as long as no one else is casting,
  //otherwise triggers the popup warning.
  $scope.showPopup = function(card) {

    //if there is an active session and no one is casting, cast the card
    if (session && !isCasting) {
      console.log(session.status);
      $scope.castCard(card);

    //if there is an active session and someone else is casting show popup
    } else if (session && isCasting) {
      $scope.showWarning = true;
      $scope.currentCard = card;
    } else if (chrome.cast) {

    //if there is no active session request one
      chrome.cast.requestSession(function(currentSession) {
        sessionListener.call(null, currentSession);

        //Provides extra time for the reciever to respond
        $timeout(function() {
          if (!isCasting) {
            $scope.castCard(card);
          } else {
            $scope.showWarning = true;
            $scope.currentCard = card;
          }
        }, 100);

      }, console.log.bind(null, 'onError: '));
    }
  };

  //clears popup warning if user cancels the cast
  $scope.cancelCast = function() {
    $scope.showWarning = false;
  };

  //Sends cast using the card that invoked showPopup. The username tracks who is currently casting
  //Passing the 'clear' parameter stops the current cast and reverts everything to default state.
  $scope.castCard = function(card, clear = false) {
    var message = {
      username: clear ? null : user,
      card: clear ? '
Welcome to CardCast!

Nothing has been casted yet...' : card.card,
      cardId: clear ? null : card._id
    };
    $scope.showWarning = false;
    session.sendMessage(namespace, JSON.stringify(message), console.log.bind(null, 'onSuccess: ', 'Message was sent: ' + message), console.log.bind(null, 'onError: '));
  };

  //Deletes selected card from the database
  $scope.deleteCard = function(card) {
    Service.deleteCard(card)
      .then(function(resp) {
        Service.getDeck()
          .then(function(resp) {
            $scope.deck = resp;
            $scope.showDelete = false;
          });
      });
  };

  $scope.warnDelete = function(card) {
    $scope.showDelete = true;
    $scope.currentCard = card;
  };

  $scope.cancelDelete = function() {
    $scope.showDelete = false;
  };

  $scope.getDeck();
});
<|endoftext|>


<commit_before>// @flow

/**
 * Display a stacked percentile chart.
 */

import React from 'react'
import {sprintf} from 'sprintf-js'
import DeepEqualComponent from '../deep-equal'
import {scalePow, scaleLinear} from 'd3-scale'
import {format} from 'd3-format'
import {line, area} from 'd3-shape'
import Boxplot from './boxplot'
import {SCENARIO, BASE, COMPARISON} from './spectrogram-selector'
import messages from '../../utils/messages'

import type {Component, Element} from 'react'

const BOX_PLOT_WIDTH = 0.1
// reversed because 5th percentile travel time has the highest accessibility
// TODO this is premised on the assumption that accessibility is done in 5-percentile steps on the backend
// That could change at some point.
// These are also used for the breaks when viewing a single scenario
const BOX_PLOT_ITEMS = [18, 14, 9, 4, 0]
const BOX_PLOT_PERCENTILES = [95, 75, 50, 25, 5]

// The plot gets too busy if we overlay two four-band plots. Instead, use a one-band plot (25th/75th pctiles)
const COMPARISON_BAND = [14, 4]
const COMPARISON_BAND_PERCENTILES = [75, 25]

const TEXT_HEIGHT = 10
const MAX_OPACITY = 0.5
const MAX_TRIP_DURATION = 120

type Props = {
  percentileCurves: number[][],
  comparisonPercentileCurves: number[][],
  isochroneCutoff: number,
  width: number,
  height: number,
  maxAccessibility: number,
  color: any,
  comparisonColor: any,
  selected: string,
  label: string,
  comparisonLabel: string
}

export default class StackedPercentile extends DeepEqualComponent<void, Props, void> {
  render (): Element {
    const {width, height, percentileCurves, color, comparisonPercentileCurves, comparisonColor, selected} = this.props

    this.yScale = this.createYScale()
    this.xScale = this.createXScale()

    const boxPlotWidth = comparisonPercentileCurves
      ? BOX_PLOT_WIDTH * width / 2
      : BOX_PLOT_WIDTH * width

    return <svg style={{width, height}}>
      <g>
        {comparisonPercentileCurves && <g transform={`translate(${boxPlotWidth})`}>
          {this.renderBoxPlot({
            percentileCurves: comparisonPercentileCurves,
            color: comparisonColor,
            width: boxPlotWidth
          })}
        </g>}
        {this.renderBoxPlot({
          percentileCurves,
          color,
          width: boxPlotWidth
        })}

        {this.renderMainPlot()}

        {selected !== BASE && this.renderCumulativeLine({
          curve: percentileCurves[9],
          color
        })}

        {selected !== SCENARIO && this.renderCumulativeLine({
          curve: comparisonPercentileCurves[9],
          color: comparisonColor
        })}

        {this.renderYAxis()}
        {this.renderXAxis()}
        {selected === COMPARISON ? this.renderComparisonLegend() : this.renderLegend()}
        {this.renderSliceLine()}
      </g>
    </svg>
  }

  /*
   * Render the curves as filled slices or as discrete curves depending on whether we're doing a
   * comparison. Filled slices are too busy when doing a comparison.
   */
  renderMainPlot () {
    const {percentileCurves, comparisonPercentileCurves, color, comparisonColor, selected} = this.props
    switch (selected) {
      case SCENARIO:
        return this.renderSlices({
          percentileCurves,
          color,
          breaks: BOX_PLOT_ITEMS
        })
      case BASE:
        return this.renderSlices({
          percentileCurves: comparisonPercentileCurves,
          color: comparisonColor,
          breaks: BOX_PLOT_ITEMS
        })
      case COMPARISON:
        return <g>
          {this.renderSlices({
            percentileCurves,
            color,
            breaks: COMPARISON_BAND
          })}
          {this.renderSlices({
            percentileCurves: comparisonPercentileCurves,
            color: comparisonColor,
            breaks: COMPARISON_BAND
          })}
        </g>
    }
  }

  renderBoxPlot ({ percentileCurves, color, width }: {
    percentileCurves: number[][],
    color: any,
    width: number
  }): Component {
    const {isochroneCutoff} = this.props
    const [low, iqrLow, med, iqrHigh, high] = BOX_PLOT_ITEMS
      .map(i => percentileCurves[i][isochroneCutoff - 1])

    return <Boxplot
      low={low}
      iqrLow={iqrLow}
      med={med}
      iqrHigh={iqrHigh}
      high={high}
      width={width}
      scale={this.yScale}
      color={color}
      />
  }

  renderYAxis (): Element {
    const {indicator, width} = this.props
    const tickFormat = format('.3s')

    // y scale
    const yTicks = this.yScale.ticks(5)

    const toRender = yTicks.map((tick, i, arr) => {
      const yoff = this.yScale(tick)

      const valueText = tickFormat(tick)

      // highest valued tick gets label, move ticks down a little so the middle of the text is on
      // the line.
      const tickText = i === arr.length - 1 ? `${valueText} ${indicator}` : valueText

      return [yoff, tickText]
    })

    return <g transform={`translate(${BOX_PLOT_WIDTH * width})`} style={{fontSize: TEXT_HEIGHT}}>
      {toRender.map(([off, text], i, arr) =>
        <text
          style={{ alignmentBaseline: i === 0 ? 'baseline' : 'middle' }}
          y={off}
          >
          {text}
        </text>)}
    </g>
  }

  renderXAxis (): Element {
    const {height} = this.props
    return <g transform={`translate(0 ${height})`}>
      {/* We don't explicitly center the '15 minutes' text; fudge the left side a bit so that the 15 appears centered */}
      {[15, 30, 45, 60, 75, 90, 105, 120].map(v => <text
        x={v === 15 ? this.xScale(v) - TEXT_HEIGHT / 2 : this.xScale(v)}
        y={0}
        style={{
          textAnchor: v === 120 ?
            'end' // don't run off end, fudge position of 120 a bit
            : v === 15
            ? 'start' // don't center the whole '15 minutes' text
            : 'middle'
        }}
        >
        {/* label first minute */}
        {v === 15 ? sprintf(messages.analysis.minutes, v) : v}
      </text>
      )}
    </g>
  }

  // TODO put this in its own DeepEqualComponent so it is not re-rendered when the cutoff changes
  /** boundaries are the boundaries between slices, as array indices in percentileCurves */
  renderSlices ({ percentileCurves, color, breaks }: {
    percentileCurves: number[][],
    color: any,
    breaks: number[]
  }): Element {
    const sliceArea = area()
      .x1((d, i) => this.xScale(i))
      .x0((d, i) => this.xScale(i))
      .y0(d => this.yScale(d[0]))
      .y1(d => this.yScale(d[1]))

    // a "slice" is the segment between two percentile curves
    const slices = []
    for (let slice = breaks.length - 1; slice > 0; slice--) {
      // slice - 1 has a higher accessibility value because it is from a less reliable travel time
      const combinedValues = percentileCurves[breaks[slice]].map((d, i) => ([d, percentileCurves[breaks[slice - 1]][i]]))
      slices.push(combinedValues)
    }

    return <g>
      {slices.map((d, i, a) => {
        const opacity = (i + 1) * MAX_OPACITY / (a.length + 1)
        return <path
          d={sliceArea(d)}
          style={{fill: color, fillOpacity: opacity}}
        />
      })}
    </g>
  }

  renderCumulativeLine ({ curve, color }: {
    curve: number[],
    color: any
  }): Element {
    const percentileLine = line()
      .x((d, i) => this.xScale(i))
      .y(d => this.yScale(d))

    return <path
      d={percentileLine(curve)}
      style={{
        stroke: color,
        strokeWidth: 0.5,
        fill: 'none'
      }}
    />
  }

  renderSliceLine (): Element {
    const {height, isochroneCutoff} = this.props
    return <line
      x1={this.xScale(isochroneCutoff)}
      x2={this.xScale(isochroneCutoff)}
      y1={0}
      y2={height}
      style={{
        stroke: '#555',
        strokeWidth: 0.5
      }} />
  }

  renderLegend (): Element {
    const {selected, color, width, height, comparisonColor} = this.props

    const currentColor = selected === SCENARIO ? color : comparisonColor
    const squareSize = TEXT_HEIGHT * 1.5
    const textOffset = squareSize * 1.5

    return <g transform={`translate(${width * 0.9} ${height * 0.7})`}>
      <text x={textOffset + TEXT_HEIGHT * 2} y={0} style={{textAnchor: 'end'}}>
        {messages.analysis.travelTimePercentile}
      </text>
      {/* Colors */}
      {BOX_PLOT_PERCENTILES.slice(1).map((d, i, a) => <rect
        x={0}
        y={i * squareSize + TEXT_HEIGHT * 1.5}
        width={squareSize}
        height={squareSize}
        style={{
          fill: currentColor,
          fillOpacity: (i + 1) * MAX_OPACITY / (a.length + 1)
        }}
        />)}
      {/* labels, subtract i from length because 95th percentile travel time should be at the bottom */}
      {BOX_PLOT_PERCENTILES.map((d: number, i: number, a: number[]) => <text
        x={textOffset}
        y={(a.length - 1 - i) * squareSize + TEXT_HEIGHT * 1.5}
        style={{
          alignmentBaseline: 'middle'
        }}
        >
        {d}
      </text>)
      }
    </g>
  }

  renderComparisonLegend (): Element {
    const { color, comparisonColor, label, width, height, comparisonLabel } = this.props
    const squareSize = TEXT_HEIGHT * 1.5
    const textOffset = squareSize * 1.5
    const titleOffset = TEXT_HEIGHT * 1.5

    return <g transform={`translate(${width * 0.9} ${height * 0.7})`}>
      <text x={textOffset + TEXT_HEIGHT * 2} y={0} style={{textAnchor: 'end'}}>
        {messages.analysis.travelTimePercentile}
      </text>

      <rect
        x={0}
        y={titleOffset}
        width={squareSize}
        height={squareSize}
        style={{
          fill: color,
          fillOpacity: 0.5 * MAX_OPACITY // value when there is only one band
        }}
      />

      <text x={textOffset} y={titleOffset} style={{alignmentBaseline: 'middle'}}>
        {/* [1] because travel time percentiles are reversed in accessibility */}
        {COMPARISON_BAND_PERCENTILES[1]}
      </text>

      <text x={textOffset} y={titleOffset + squareSize} style={{alignmentBaseline: 'middle'}}>
        {/* [1] because travel time percentiles are reversed in accessibility */}
        {COMPARISON_BAND_PERCENTILES[0]}
      </text>

      {/* variant label */}
      <text
        x={-1 * (textOffset - squareSize)}
        y={titleOffset + squareSize / 2}
        style={{
          alignmentBaseline: 'middle',
          textAnchor: 'end'
        }} >
        {label}
      </text>

      <rect
        x={0}
        y={titleOffset + squareSize * 3}
        width={squareSize}
        height={squareSize}
        style={{
          fill: comparisonColor,
          fillOpacity: 0.5 * MAX_OPACITY // value when there is only one band
        }}
      />

      <text x={textOffset} y={titleOffset + squareSize * 3} style={{alignmentBaseline: 'middle'}}>
        {/* [1] because travel time percentiles are reversed in accessibility */}
        {COMPARISON_BAND_PERCENTILES[1]}
      </text>

      <text x={textOffset} y={titleOffset + squareSize * 4} style={{alignmentBaseline: 'middle'}}>
        {/* [1] because travel time percentiles are reversed in accessibility */}
        {COMPARISON_BAND_PERCENTILES[0]}
      </text>

      {/* variant label */}
      <text
        x={-1 * (textOffset - squareSize)}
        y={titleOffset + squareSize * 3 + squareSize / 2}
        style={{
          alignmentBaseline: 'middle',
          textAnchor: 'end'
        }} >
        {comparisonLabel}
      </text>
    </g>
  }

  createYScale (): (number) => number {
    const {height, maxAccessibility} = this.props
    return scalePow()
      .exponent(0.5)
      .domain([0, maxAccessibility])
      .range([height, 0])
  }

  createXScale (): (number) => number {
    const {width} = this.props
    return scaleLinear()
      .domain([0, MAX_TRIP_DURATION - 1])
      .range([BOX_PLOT_WIDTH * width, width])
  }
}
<commit_msg>fix(analysis): don't allow y-axis ticks to run off top of screen.<commit_after>// @flow

/**
 * Display a stacked percentile chart.
 */

import React from 'react'
import {sprintf} from 'sprintf-js'
import DeepEqualComponent from '../deep-equal'
import {scalePow, scaleLinear} from 'd3-scale'
import {format} from 'd3-format'
import {line, area} from 'd3-shape'
import Boxplot from './boxplot'
import {SCENARIO, BASE, COMPARISON} from './spectrogram-selector'
import messages from '../../utils/messages'

import type {Component, Element} from 'react'

const BOX_PLOT_WIDTH = 0.1
// reversed because 5th percentile travel time has the highest accessibility
// TODO this is premised on the assumption that accessibility is done in 5-percentile steps on the backend
// That could change at some point.
// These are also used for the breaks when viewing a single scenario
const BOX_PLOT_ITEMS = [18, 14, 9, 4, 0]
const BOX_PLOT_PERCENTILES = [95, 75, 50, 25, 5]

// The plot gets too busy if we overlay two four-band plots. Instead, use a one-band plot (25th/75th pctiles)
const COMPARISON_BAND = [14, 4]
const COMPARISON_BAND_PERCENTILES = [75, 25]

const TEXT_HEIGHT = 10
const MAX_OPACITY = 0.5
const MAX_TRIP_DURATION = 120

type Props = {
  percentileCurves: number[][],
  comparisonPercentileCurves: number[][],
  isochroneCutoff: number,
  width: number,
  height: number,
  maxAccessibility: number,
  color: any,
  comparisonColor: any,
  selected: string,
  label: string,
  comparisonLabel: string
}

export default class StackedPercentile extends DeepEqualComponent<void, Props, void> {
  render (): Element {
    const {width, height, percentileCurves, color, comparisonPercentileCurves, comparisonColor, selected} = this.props

    this.yScale = this.createYScale()
    this.xScale = this.createXScale()

    const boxPlotWidth = comparisonPercentileCurves
      ? BOX_PLOT_WIDTH * width / 2
      : BOX_PLOT_WIDTH * width

    return <svg style={{width, height}}>
      <g>
        {comparisonPercentileCurves && <g transform={`translate(${boxPlotWidth})`}>
          {this.renderBoxPlot({
            percentileCurves: comparisonPercentileCurves,
            color: comparisonColor,
            width: boxPlotWidth
          })}
        </g>}
        {this.renderBoxPlot({
          percentileCurves,
          color,
          width: boxPlotWidth
        })}

        {this.renderMainPlot()}

        {selected !== BASE && this.renderCumulativeLine({
          curve: percentileCurves[9],
          color
        })}

        {selected !== SCENARIO && this.renderCumulativeLine({
          curve: comparisonPercentileCurves[9],
          color: comparisonColor
        })}

        {this.renderYAxis()}
        {this.renderXAxis()}
        {selected === COMPARISON ? this.renderComparisonLegend() : this.renderLegend()}
        {this.renderSliceLine()}
      </g>
    </svg>
  }

  /*
   * Render the curves as filled slices or as discrete curves depending on whether we're doing a
   * comparison. Filled slices are too busy when doing a comparison.
   */
  renderMainPlot () {
    const {percentileCurves, comparisonPercentileCurves, color, comparisonColor, selected} = this.props
    switch (selected) {
      case SCENARIO:
        return this.renderSlices({
          percentileCurves,
          color,
          breaks: BOX_PLOT_ITEMS
        })
      case BASE:
        return this.renderSlices({
          percentileCurves: comparisonPercentileCurves,
          color: comparisonColor,
          breaks: BOX_PLOT_ITEMS
        })
      case COMPARISON:
        return <g>
          {this.renderSlices({
            percentileCurves,
            color,
            breaks: COMPARISON_BAND
          })}
          {this.renderSlices({
            percentileCurves: comparisonPercentileCurves,
            color: comparisonColor,
            breaks: COMPARISON_BAND
          })}
        </g>
    }
  }

  renderBoxPlot ({ percentileCurves, color, width }: {
    percentileCurves: number[][],
    color: any,
    width: number
  }): Component {
    const {isochroneCutoff} = this.props
    const [low, iqrLow, med, iqrHigh, high] = BOX_PLOT_ITEMS
      .map(i => percentileCurves[i][isochroneCutoff - 1])

    return <Boxplot
      low={low}
      iqrLow={iqrLow}
      med={med}
      iqrHigh={iqrHigh}
      high={high}
      width={width}
      scale={this.yScale}
      color={color}
      />
  }

  renderYAxis (): Element {
    const {indicator, width, height} = this.props
    const tickFormat = format('.3s')

    // make sure that the top tick is not off the screen
    const maxYValueWithTextOnScreen = this.yScale.invert(TEXT_HEIGHT / 2)
    const trimmedYScale = scalePow()
      .exponent(0.5)
      .domain([0, maxYValueWithTextOnScreen])
      .range([height, TEXT_HEIGHT / 2])

    // y scale
    const yTicks = trimmedYScale.ticks(5)

    const toRender = yTicks.map((tick, i, arr) => {
      const yoff = this.yScale(tick)

      const valueText = tickFormat(tick)

      // highest valued tick gets label, move ticks down a little so the middle of the text is on
      // the line.
      const tickText = i === arr.length - 1 ? `${valueText} ${indicator}` : valueText

      return [yoff, tickText]
    })

    return <g transform={`translate(${BOX_PLOT_WIDTH * width})`} style={{fontSize: TEXT_HEIGHT}}>
      {toRender.map(([off, text], i, arr) =>
        <text
          style={{ alignmentBaseline: i === 0 ? 'baseline' : 'middle' }}
          y={off}
          >
          {text}
        </text>)}
    </g>
  }

  renderXAxis (): Element {
    const {height} = this.props
    return <g transform={`translate(0 ${height})`}>
      {/* We don't explicitly center the '15 minutes' text; fudge the left side a bit so that the 15 appears centered */}
      {[15, 30, 45, 60, 75, 90, 105, 120].map(v => <text
        x={v === 15 ? this.xScale(v) - TEXT_HEIGHT / 2 : this.xScale(v)}
        y={0}
        style={{
          textAnchor: v === 120
            ? 'end' // don't run off end, fudge position of 120 a bit
            : v === 15
            ? 'start' // don't center the whole '15 minutes' text
            : 'middle'
        }}
        >
        {/* label first minute */}
        {v === 15 ? sprintf(messages.analysis.minutes, v) : v}
      </text>
      )}
    </g>
  }

  // TODO put this in its own DeepEqualComponent so it is not re-rendered when the cutoff changes
  /** boundaries are the boundaries between slices, as array indices in percentileCurves */
  renderSlices ({ percentileCurves, color, breaks }: {
    percentileCurves: number[][],
    color: any,
    breaks: number[]
  }): Element {
    const sliceArea = area()
      .x1((d, i) => this.xScale(i))
      .x0((d, i) => this.xScale(i))
      .y0(d => this.yScale(d[0]))
      .y1(d => this.yScale(d[1]))

    // a "slice" is the segment between two percentile curves
    const slices = []
    for (let slice = breaks.length - 1; slice > 0; slice--) {
      // slice - 1 has a higher accessibility value because it is from a less reliable travel time
      const combinedValues = percentileCurves[breaks[slice]].map((d, i) => ([d, percentileCurves[breaks[slice - 1]][i]]))
      slices.push(combinedValues)
    }

    return <g>
      {slices.map((d, i, a) => {
        const opacity = (i + 1) * MAX_OPACITY / (a.length + 1)
        return <path
          d={sliceArea(d)}
          style={{fill: color, fillOpacity: opacity}}
        />
      })}
    </g>
  }

  renderCumulativeLine ({ curve, color }: {
    curve: number[],
    color: any
  }): Element {
    const percentileLine = line()
      .x((d, i) => this.xScale(i))
      .y(d => this.yScale(d))

    return <path
      d={percentileLine(curve)}
      style={{
        stroke: color,
        strokeWidth: 0.5,
        fill: 'none'
      }}
    />
  }

  renderSliceLine (): Element {
    const {height, isochroneCutoff} = this.props
    return <line
      x1={this.xScale(isochroneCutoff)}
      x2={this.xScale(isochroneCutoff)}
      y1={0}
      y2={height}
      style={{
        stroke: '#555',
        strokeWidth: 0.5
      }} />
  }

  renderLegend (): Element {
    const {selected, color, width, height, comparisonColor} = this.props

    const currentColor = selected === SCENARIO ? color : comparisonColor
    const squareSize = TEXT_HEIGHT * 1.5
    const textOffset = squareSize * 1.5

    return <g transform={`translate(${width * 0.9} ${height * 0.7})`}>
      <text x={textOffset + TEXT_HEIGHT * 2} y={0} style={{textAnchor: 'end'}}>
        {messages.analysis.travelTimePercentile}
      </text>
      {/* Colors */}
      {BOX_PLOT_PERCENTILES.slice(1).map((d, i, a) => <rect
        x={0}
        y={i * squareSize + TEXT_HEIGHT * 1.5}
        width={squareSize}
        height={squareSize}
        style={{
          fill: currentColor,
          fillOpacity: (i + 1) * MAX_OPACITY / (a.length + 1)
        }}
        />)}
      {/* labels, subtract i from length because 95th percentile travel time should be at the bottom */}
      {BOX_PLOT_PERCENTILES.map((d: number, i: number, a: number[]) => <text
        x={textOffset}
        y={(a.length - 1 - i) * squareSize + TEXT_HEIGHT * 1.5}
        style={{
          alignmentBaseline: 'middle'
        }}
        >
        {d}
      </text>)
      }
    </g>
  }

  renderComparisonLegend (): Element {
    const { color, comparisonColor, label, width, height, comparisonLabel } = this.props
    const squareSize = TEXT_HEIGHT * 1.5
    const textOffset = squareSize * 1.5
    const titleOffset = TEXT_HEIGHT * 1.5

    return <g transform={`translate(${width * 0.9} ${height * 0.7})`}>
      <text x={textOffset + TEXT_HEIGHT * 2} y={0} style={{textAnchor: 'end'}}>
        {messages.analysis.travelTimePercentile}
      </text>

      <rect
        x={0}
        y={titleOffset}
        width={squareSize}
        height={squareSize}
        style={{
          fill: color,
          fillOpacity: 0.5 * MAX_OPACITY // value when there is only one band
        }}
      />

      <text x={textOffset} y={titleOffset} style={{alignmentBaseline: 'middle'}}>
        {/* [1] because travel time percentiles are reversed in accessibility */}
        {COMPARISON_BAND_PERCENTILES[1]}
      </text>

      <text x={textOffset} y={titleOffset + squareSize} style={{alignmentBaseline: 'middle'}}>
        {/* [1] because travel time percentiles are reversed in accessibility */}
        {COMPARISON_BAND_PERCENTILES[0]}
      </text>

      {/* variant label */}
      <text
        x={-1 * (textOffset - squareSize)}
        y={titleOffset + squareSize / 2}
        style={{
          alignmentBaseline: 'middle',
          textAnchor: 'end'
        }} >
        {label}
      </text>

      <rect
        x={0}
        y={titleOffset + squareSize * 3}
        width={squareSize}
        height={squareSize}
        style={{
          fill: comparisonColor,
          fillOpacity: 0.5 * MAX_OPACITY // value when there is only one band
        }}
      />

      <text x={textOffset} y={titleOffset + squareSize * 3} style={{alignmentBaseline: 'middle'}}>
        {/* [1] because travel time percentiles are reversed in accessibility */}
        {COMPARISON_BAND_PERCENTILES[1]}
      </text>

      <text x={textOffset} y={titleOffset + squareSize * 4} style={{alignmentBaseline: 'middle'}}>
        {/* [1] because travel time percentiles are reversed in accessibility */}
        {COMPARISON_BAND_PERCENTILES[0]}
      </text>

      {/* variant label */}
      <text
        x={-1 * (textOffset - squareSize)}
        y={titleOffset + squareSize * 3 + squareSize / 2}
        style={{
          alignmentBaseline: 'middle',
          textAnchor: 'end'
        }} >
        {comparisonLabel}
      </text>
    </g>
  }

  createYScale (): (number) => number {
    const {height, maxAccessibility} = this.props
    return scalePow()
      .exponent(0.5)
      .domain([0, maxAccessibility])
      .range([height, 0])
  }

  createXScale (): (number) => number {
    const {width} = this.props
    return scaleLinear()
      .domain([0, MAX_TRIP_DURATION - 1])
      .range([BOX_PLOT_WIDTH * width, width])
  }
}
<|endoftext|>


<commit_before>import path from 'path';
import fs from 'fs-extra';
import findUp from 'find-up';
import resolveFrom from 'resolve-from';

import { logger } from '@storybook/node-logger';

import loadPresets from '../presets';
import loadCustomPresets from '../common/custom-presets';
import { typeScriptDefaults } from '../config/defaults';

const getAutoRefs = async (options) => {
  const location = await findUp('package.json', { cwd: options.configDir });
  const directory = path.dirname(location);

  const { dependencies, devDependencies } = await fs.readJSON(location);

  const list = await Promise.all(
    Object.keys({ ...dependencies, ...devDependencies }).map(async (d) => {
      try {
        const l = resolveFrom(directory, path.join(d, 'package.json'));

        const { storybook, name } = await fs.readJSON(l);

        if (storybook?.url) {
          return { id: name, ...storybook };
        }
      } catch {
        logger.warn(`unable to find package.json for ${d}`);
        return undefined;
      }
      return undefined;
    })
  );

  return list.filter(Boolean);
};

const stripTrailingSlash = (url) => url.replace(/\/$/, '');

const toTitle = (input) => {
  const result = input
    .replace(/[A-Z]/g, (f) => ` ${f}`)
    .replace(/[-_][A-Z]/gi, (f) => ` ${f.toUppercase()}`)
    .replace('-', ' ')
    .replace('_', ' ');

  return `${result.substring(0, 1).toUpperCase()}${result.substring(1)}`.trim();
};

async function getManagerWebpackConfig(options, presets) {
  const typescriptOptions = await presets.apply('typescript', { ...typeScriptDefaults }, options);
  const babelOptions = await presets.apply('babel', {}, { ...options, typescriptOptions });

  const autoRefs = await getAutoRefs(options);
  const definedRefs = await presets.apply('refs', undefined, options);
  const entries = await presets.apply('managerEntries', [], options);

  const refs = {};

  if (autoRefs && autoRefs.length) {
    autoRefs.forEach(({ id, url, title }) => {
      refs[id] = {
        id,
        url: stripTrailingSlash(url),
        title,
      };
    });
  }

  if (definedRefs) {
    Object.entries(refs).forEach(([key, value]) => {
      const url = typeof value === 'string' ? value : value.url;
      const title = typeof value === 'string' ? toTitle(key) : value.title || toTitle(value.key);

      refs[key] = {
        id: key,
        title,
        url: stripTrailingSlash(url),
      };
    });
  }
  if (autoRefs || definedRefs) {
    entries.push(path.resolve(path.join(options.configDir, `generated-refs.js`)));
  }

  return presets.apply('managerWebpack', {}, { ...options, babelOptions, entries, refs });
}

export default async (options) => {
  const { corePresets = [], frameworkPresets = [], overridePresets = [], ...restOptions } = options;

  const presetsConfig = [
    ...corePresets,
    require.resolve('../common/babel-cache-preset.js'),
    ...frameworkPresets,
    ...loadCustomPresets(options),
    ...overridePresets,
  ];

  const presets = loadPresets(presetsConfig, restOptions);

  return getManagerWebpackConfig({ ...restOptions, presets }, presets);
};
<commit_msg>FIX manual refs<commit_after>import path from 'path';
import fs from 'fs-extra';
import findUp from 'find-up';
import resolveFrom from 'resolve-from';

import { logger } from '@storybook/node-logger';

import loadPresets from '../presets';
import loadCustomPresets from '../common/custom-presets';
import { typeScriptDefaults } from '../config/defaults';

const getAutoRefs = async (options) => {
  const location = await findUp('package.json', { cwd: options.configDir });
  const directory = path.dirname(location);

  const { dependencies, devDependencies } = await fs.readJSON(location);

  const list = await Promise.all(
    Object.keys({ ...dependencies, ...devDependencies }).map(async (d) => {
      try {
        const l = resolveFrom(directory, path.join(d, 'package.json'));

        const { storybook, name } = await fs.readJSON(l);

        if (storybook?.url) {
          return { id: name, ...storybook };
        }
      } catch {
        logger.warn(`unable to find package.json for ${d}`);
        return undefined;
      }
      return undefined;
    })
  );

  return list.filter(Boolean);
};

const stripTrailingSlash = (url) => url.replace(/\/$/, '');

const toTitle = (input) => {
  const result = input
    .replace(/[A-Z]/g, (f) => ` ${f}`)
    .replace(/[-_][A-Z]/gi, (f) => ` ${f.toUppercase()}`)
    .replace('-', ' ')
    .replace('_', ' ');

  return `${result.substring(0, 1).toUpperCase()}${result.substring(1)}`.trim();
};

async function getManagerWebpackConfig(options, presets) {
  const typescriptOptions = await presets.apply('typescript', { ...typeScriptDefaults }, options);
  const babelOptions = await presets.apply('babel', {}, { ...options, typescriptOptions });

  const autoRefs = await getAutoRefs(options);
  const definedRefs = await presets.apply('refs', undefined, options);
  const entries = await presets.apply('managerEntries', [], options);

  const refs = {};

  if (autoRefs && autoRefs.length) {
    autoRefs.forEach(({ id, url, title }) => {
      refs[id] = {
        id,
        url: stripTrailingSlash(url),
        title,
      };
    });
  }

  if (definedRefs) {
    Object.entries(definedRefs).forEach(([key, value]) => {
      const url = typeof value === 'string' ? value : value.url;
      const title = typeof value === 'string' ? toTitle(key) : value.title || toTitle(value.key);

      refs[key] = {
        id: key,
        title,
        url: stripTrailingSlash(url),
      };
    });
  }
  if (autoRefs || definedRefs) {
    entries.push(path.resolve(path.join(options.configDir, `generated-refs.js`)));
  }

  return presets.apply('managerWebpack', {}, { ...options, babelOptions, entries, refs });
}

export default async (options) => {
  const { corePresets = [], frameworkPresets = [], overridePresets = [], ...restOptions } = options;

  const presetsConfig = [
    ...corePresets,
    require.resolve('../common/babel-cache-preset.js'),
    ...frameworkPresets,
    ...loadCustomPresets(options),
    ...overridePresets,
  ];

  const presets = loadPresets(presetsConfig, restOptions);

  return getManagerWebpackConfig({ ...restOptions, presets }, presets);
};
<|endoftext|>


<commit_before>var ctx = com.mojang.minecraftpe.MainActivity.currentMainActivity.get();
var root = new java.io.File(android.os.Environment.getExternalStorageDirectory().getAbsoluteFile(), "games/com.mojang/minecraftResources/LOCK2.0");

function runOnThread(func){
	new java.lang.Thread({run: func}).start();
}

function runOnUiThread(func){
	ctx.runOnUiThread(new java.lang.Runnable({run: func}));
}

var Conditions = {
		enabled: false,
		activated: false
};

var Resources = {
		mediaPlayer: null,

		startMusic: function(musicName){
			var file = new java.io.File(root, musicName);
			if(file.exists() === false){
				clientMessage("음악 파일이 존재하지 않습니다.");
				return;
			}

			try{
				if(Resources.mediaPlayer.isPlaying()){
					Resources.mediaPlayer.pause();
				}
				Resources.mediaPlayer.reset();
				Resources.mediaPlayer.setDataSource(file.getAbsolutePath());
				Resources.mediaPlayer.prepare();
				Resources.mediaPlayer.start();
			}catch(e){
				clientMessage("음악 파일 재생에 실패했습니다. \n" + e.getMessage());
			}
		},

		stopMusic: function(){
			if(Resources.mediaPlayer.isPlaying()){
				Resources.mediaPlayer.stop();
				Resources.mediaPlayer.prepare();
				Resources.mediaPlayer.seekTo(0);
			}
		},

		finishMusic: function(){
			runOnUiThread(function(){
				if(Resources.mediaPlayer !== null){
					Resources.mediaPlayer.release();
					Resources.mediaPlayer = null;
				}
			});
		}
};

var GUI = {
		window: null,
		imageViewA: null,

		create: function(){runOnUiThread(function(){
			try{
				var layout = new android.widget.FrameLayout(ctx);
				layout.setLayoutParams(new android.widget.FrameLayout.LayoutParams(android.widget.FrameLayout.LayoutParams.MATCH_PARENT, android.widget.FrameLayout.LayoutParams.MATCH_PARENT));

				GUI.imageViewA = new android.widget.ImageView(ctx);
				GUI.imageViewA.setLayoutParams(new android.widget.FrameLayout.LayoutParams(android.widget.FrameLayout.LayoutParams.WRAP_CONTENT, android.widget.FrameLayout.LayoutParams.WRAP_CONTENT));

				layout.addView(GUI.imageViewA);

				GUI.window = new android.widget.PopupWindow(layout, ctx.getWindowManager().getDefaultDisplay().getWidth(), ctx.getWindowManager().getDefaultDisplay().getHeight(), true);
				GUI.window.setTouchable(false);
				GUI.window.setFocusable(false);
				GUI.window.setOutsideTouchable(true);
				GUI.window.showAtLocation(ctx.getWindow().getDecorView(), android.view.Gravity.CENTER, 0, 0);
			}catch(e){
				clientMessage("GUI 생성에 실패했습니다. \n" + e.getMessage());
			}
		});},

		remove: function(){runOnUiThread(function(){
			if(GUI.window !== null){
				GUI.window.dismiss();
				GUI.window = null;
			}
		});},

		setImage: function(drawable){runOnUiThread(function(){
			if(GUI.imageViewA !== null){
				GUI.imageViewA.setBackgroundDrawable(drawable);
			}
		});},

		createDrawable: function(drawableName){
			var file = new java.io.File(root, drawableName);
			if(file.exists() === false){
				clientMessage("이미지 파일이 존재하지 않습니다.");
				return null;
			}

			try{
				return android.graphics.drawable.Drawable.createFromPath(file.getAbsolutePath());
			}catch(e){
				clientMessage("이미지 로드에 실패했습니다. \n" + e.getMessage());
			}
		}
};

function init(){runOnThread(function(){
	Resources.mediaPlayer = new android.media.MediaPlayer();

	Resources.NONE = GUI.createDrawable("NOSCREEN.png");
	Resources.READY = GUI.createDrawable("LOCKREADY.png");

	Resources.ACTIVE_ANIMATED = new android.graphics.drawable.AnimationDrawable();
	Resources.ACTIVE_ANIMATED.setOneShot(true);

	for(var index = 0; index < 60; index++){
		Resources.ACTIVE_ANIMATED.addFrame(GUI.createDrawable("LOCKON/LOCK" + index + ".png"), 32);
	}

	runOnUiThread(function(){
		android.widget.Toast.makeText(ctx, "© 2014 ChalkPE. All rights reserved.", 1).show();
	});
});}

function finalize(){
	GUI.remove();
	Resources.finishMusic();
}

function getCoordinateArray(ent){
	return [Entity.getX(ent), Entity.getY(ent), Entity.getZ(ent)];
}

function killEntity(ent){
	Entity.setHealth(ent, 1);
	Entity.setFireTicks(ent, 15);

	var blockId = Level.getTile.apply(null, getCoordinateArray(ent));
	if(Entity.getEntityTypeId(ent) === 36 || (blockId === 8 || blockId === 9)){
		Entity.setHealth(ent, 0);
	}

	Level.playSound(Entity.getX(ent), Entity.getY(ent), Entity.getZ(ent), "random.explode", 2, 1);
}

function isCloseArray(a, b, length){
	return Math.abs(a[0] - b[0]) < length &&
		Math.abs(a[1] - b[1]) < length &&
		Math.abs(a[2] - b[2]) < length;
}

function getLookingEntity(){
	var radian =  1 / 180 * Math.PI;
	var player = Player.getEntity();

	var centerX = Entity.getX(player),
		centerY = Entity.getY(player),
		centerZ = Entity.getZ(player);

	var yaw = Entity.getYaw(player),
		pitch = Entity.getPitch(player);

	for(var radius = 0; radius < 64; radius++){
		var coordinate = [
			Math.floor(centerX + radius * -Math.sin(yaw * radian) * Math.cos(pitch * radian)),
			Math.floor(centerY + radius * -Math.sin(pitch * radian)),
			Math.floor(centerZ + radius * Math.cos(yaw * radian) * Math.cos(pitch * radian))
		];

		var entites = Entity.getAll();

		for(var i = 0; i < entites.length; i++){
			if(entites[i] !== player && isClose(getCoordinateArray(entites[i]), coordinate, 2)){
				return entites[i];
			}
		}
	}
	return null;
}

function onTick(){
	var enabled = (Player.getCarriedItem() === 261);

	if(Conditions.enabled === true && enabled === false){ //READY -> NONE
		Resources.stopMusic();
		GUI.setImage(Resources.NONE);
		Resources.ACTIVE_ANIMATED.stop();

		Conditions.enabled = enabled;
		Conditions.activated = false;
	}else if(Conditions.enabled === false && enabled === true){ //NONE -> READY
		Resources.startMusic();
		GUI.setImage(Resources.READY);

		Conditions.enabled = enabled;
		Conditions.activated = false;
	}else{
		var looking = getLookingEntity();
		var activated = looking !== null;

		if(Conditions.activated === false && activated === true){ //READY -> ACTIVE
			GUI.setImage(Resources.ACTIVE_ANIMATED);
			Resources.ACTIVE_ANIMATED.start();

			Conditions.activated = activated;
		}else if(Conditions.activated === true && activated === false){ //ACTIVE -> READY
			Resources.ACTIVE_ANIMATED.stop();
			GUI.setImage(Resources.READY);

			Conditions.activated = activated;
		}
	}
}

function onArrowShot(ent){
	if(isClose(getCoordinateArray(ent), getCoordinateArray(Player.getEntity()), 2) === false){
		//플레이어와 2블럭 이상 떨어진 곳에서 스폰된 화살은 무시
		return;
	}

	var looking = getLookingEntity();
	if(looking !== null){
		Entity.remove(ent);
		killEntity(looking);
	}
}

function newLevel(){
	init();
}

function leaveGame(){
	finalize();
}

function modTick(){
	onTick();
}

function entityAddedHook(ent){
	if(Entity.getEntityTypeId(ent) == 80){
		onArrowShot(ent);
	}
}<commit_msg>락온스크립트 수정<commit_after>var ctx = com.mojang.minecraftpe.MainActivity.currentMainActivity.get();
var root = new java.io.File(android.os.Environment.getExternalStorageDirectory().getAbsoluteFile(), "games/com.mojang/minecraftResources/LOCK2.0");

var tick = 10;

function runOnThread(func){
	new java.lang.Thread({run: func}).start();
}

function runOnUiThread(func){
	ctx.runOnUiThread(new java.lang.Runnable({run: func}));
}

var Conditions = {
		enabled: false,
		activated: false
};

var Resources = {
		mediaPlayer: null,

		startMusic: function(musicName){
			var file = new java.io.File(root, musicName);
			if(file.exists() === false){
				clientMessage("음악 파일이 존재하지 않습니다.");
				return;
			}

			try{
				if(Resources.mediaPlayer.isPlaying()){
					Resources.mediaPlayer.pause();
				}
				Resources.mediaPlayer.reset();
				Resources.mediaPlayer.setDataSource(file.getAbsolutePath());
				Resources.mediaPlayer.prepare();
				Resources.mediaPlayer.start();
			}catch(e){
				clientMessage("음악 파일 재생에 실패했습니다. \n" + e.getMessage());
			}
		},

		stopMusic: function(){
			if(Resources.mediaPlayer.isPlaying()){
				Resources.mediaPlayer.stop();
				Resources.mediaPlayer.prepare();
				Resources.mediaPlayer.seekTo(0);
			}
		},

		finishMusic: function(){
			runOnUiThread(function(){
				if(Resources.mediaPlayer !== null){
					Resources.mediaPlayer.release();
					Resources.mediaPlayer = null;
				}
			});
		}
};

var GUI = {
		window: null,
		imageViewA: null,

		create: function(){runOnUiThread(function(){
			try{
				var layout = new android.widget.FrameLayout(ctx);
				layout.setLayoutParams(new android.widget.FrameLayout.LayoutParams(android.widget.FrameLayout.LayoutParams.MATCH_PARENT, android.widget.FrameLayout.LayoutParams.MATCH_PARENT));

				GUI.imageViewA = new android.widget.ImageView(ctx);
				GUI.imageViewA.setLayoutParams(new android.widget.FrameLayout.LayoutParams(android.widget.FrameLayout.LayoutParams.MATCH_PARENT, android.widget.FrameLayout.LayoutParams.MATCH_PARENT));

				layout.addView(GUI.imageViewA);

				GUI.window = new android.widget.PopupWindow(layout, ctx.getWindowManager().getDefaultDisplay().getWidth(), ctx.getWindowManager().getDefaultDisplay().getHeight(), true);
				GUI.window.setTouchable(false);
				GUI.window.setFocusable(false);
				GUI.window.setOutsideTouchable(true);
				GUI.window.showAtLocation(ctx.getWindow().getDecorView(), android.view.Gravity.CENTER, 0, 0);
			}catch(e){
				clientMessage("GUI 생성에 실패했습니다. \n" + e.getMessage());
			}
		});},

		remove: function(){runOnUiThread(function(){
			if(GUI.window !== null){
				GUI.window.dismiss();
				GUI.window = null;
			}
		});},

		setImage: function(drawable){runOnUiThread(function(){
			if(GUI.imageViewA !== null){
				GUI.imageViewA.setBackgroundDrawable(drawable);
			}
		});},

		createDrawable: function(drawableName){
			var file = new java.io.File(root, drawableName);
			if(file.exists() === false){
				clientMessage("이미지 파일이 존재하지 않습니다.");
				return null;
			}

			try{
				/*var bitmap = android.graphics.BitmapFactory.decodeFile(file.getAbsolutePath());
				var height = ctx.getWindowManager().getDefaultDisplay().getHeight();
				var scale = height / bitmap.getHeight();
				var width = scale * bitmap.getWidth(); //ctx.getWindowManager().getDefaultDisplay().getWidth();

				return new android.graphics.drawable.BitmapDrawable(android.graphics.Bitmap.createScaledBitmap(bitmap, width, height, true));
				*/
				return android.graphics.drawable.Drawable.createFromPath(file.getAbsolutePath());
			}catch(e){
				clientMessage("이미지 로드에 실패했습니다. \n" + e.getMessage());
			}
		}
};

function init(){runOnThread(function(){
	Resources.mediaPlayer = new android.media.MediaPlayer();

	Resources.NONE = GUI.createDrawable("NOSCREEN.png");
	Resources.READY = GUI.createDrawable("LOCKREADY.png");

	Resources.ACTIVE_ANIMATED = new android.graphics.drawable.AnimationDrawable();
	Resources.ACTIVE_ANIMATED.setOneShot(true);
	Resources.ACTIVE_ANIMATED.setVisible(true, true);

	for(var index = 0; index < 60; index++){
		Resources.ACTIVE_ANIMATED.addFrame(GUI.createDrawable("LOCKON/LOCK" + index + ".png"), 32);
	}

	GUI.create();

	runOnUiThread(function(){
		android.widget.Toast.makeText(ctx, "© 2014 ChalkPE. All rights reserved.", 1).show();
	});
});}

function finalize(){
	GUI.remove();
	Resources.finishMusic();
}

function getCoordinateArray(ent){
	return [Entity.getX(ent), Entity.getY(ent), Entity.getZ(ent)];
}

function killEntity(ent){
	Entity.setHealth(ent, 1);
	Entity.setFireTicks(ent, 15);

	var blockId = Level.getTile.apply(null, getCoordinateArray(ent));
	if(Entity.getEntityTypeId(ent) === 36 || (blockId === 8 || blockId === 9)){
		Entity.setHealth(ent, 0);
	}

	Level.playSound(Entity.getX(ent), Entity.getY(ent), Entity.getZ(ent), "random.explode", 2, 1);
}

function isClose(a, b, length){
	return Math.abs(a[0] - b[0]) < length &&
	Math.abs(a[1] - b[1]) < length &&
	Math.abs(a[2] - b[2]) < length;
}

function getLookingEntity(){
	try{
		var radian =  1 / 180 * Math.PI;
		var player = Player.getEntity();

		var centerX = Entity.getX(player),
		centerY = Entity.getY(player),
		centerZ = Entity.getZ(player);

		var yaw = Entity.getYaw(player),
		pitch = Entity.getPitch(player);

		for(var radius = 0; radius < 100; radius += 2){
			var coordinate = [
			                  Math.floor(centerX + radius * -Math.sin(yaw * radian) * Math.cos(pitch * radian)),
			                  Math.floor(centerY + radius * -Math.sin(pitch * radian)),
			                  Math.floor(centerZ + radius * Math.cos(yaw * radian) * Math.cos(pitch * radian))
			                  ];

			var entites = Entity.getAll();

			for(var i = 0; i < entites.length; i++){
				if(entites[i] !== player && Entity.getEntityTypeId(entites[i]) < 64 &&
						isClose(getCoordinateArray(entites[i]), coordinate, 2)){
					return entites[i];
				}
			}
		}
		return null;
	}catch(e){
		return null;
	}
}

function onTick(){
	var enabled = (Player.getCarriedItem() === 261);

	if(Conditions.enabled === false && enabled === false){ //NONE -> NONE

	}else if(Conditions.enabled === true && enabled === false){ //READY -> NONE
		Resources.stopMusic();
		GUI.setImage(Resources.NONE);
		runOnUiThread(function(){
			Resources.ACTIVE_ANIMATED.stop();
		});

		Conditions.enabled = enabled;
		Conditions.activated = false;
	}else if(Conditions.enabled === false && enabled === true){ //NONE -> READY
		GUI.setImage(Resources.READY);

		Conditions.enabled = enabled;
		Conditions.activated = false;
	}else{
		var looking = getLookingEntity();
		var activated = looking !== null;

		if(Conditions.activated === false && activated === true){ //READY -> ACTIVATED
			Resources.startMusic("lockon.mp3");

			GUI.setImage(Resources.ACTIVE_ANIMATED);
			runOnUiThread(function(){
				Resources.ACTIVE_ANIMATED.start();
			});

			Conditions.activated = activated;
		}else if(Conditions.activated === true && activated === false){ //ACTIVATED -> READY
			Resources.stopMusic();

			GUI.setImage(Resources.READY);
			runOnUiThread(function(){
				Resources.ACTIVE_ANIMATED.stop();
			});

			Conditions.activated = activated;
		}
	}
}

function onArrowShot(ent){
	if(Conditions.activated === false){
		return;
	}

	if(isClose(getCoordinateArray(ent), getCoordinateArray(Player.getEntity()), 2) === false){
		//플레이어와 2블럭 이상 떨어진 곳에서 스폰된 화살은 무시
		return;
	}

	var looking = getLookingEntity();
	if(looking !== null){
		Entity.remove(ent);
		killEntity(looking);
	}
}

function newLevel(){
	init();
}

function leaveGame(){
	finalize();
}

function modTick(){
	if(--tick){return;} tick = 10;
	runOnThread(onTick);
}

function entityAddedHook(ent){
	if(Entity.getEntityTypeId(ent) == 80){
		onArrowShot(ent);
	}
}<|endoftext|>


<commit_before>var assert = require('assert');

module.exports = function() { };

module.exports.prototype = {
  configure: function(option) {
    assert(option === true, this.getOptionName() + ' requires a true value');
  },

  getOptionName: function() {
    return 'requireEnhancedObjectLiterals';
  },

  check: function(file, errors) {
    file.iterateNodesByType('Property', function(node) {
      // node.key.name is used when the property key is an unquoted identifier
      // node.key.value is used when the property key is a quoted string
      var propertyName = node.key.name || node.key.value;
      var valueName = node.value.name;
      var shorthand = node.shorthand;

      // check for non-shorthand properties
      if (propertyName && propertyName === valueName && !shorthand) {
        errors.add(
          'Property assignment should use enhanced object literal function. `{ propName: propName}` is not allowed.',
          node.loc.start
        );
      }

      // check for non-method function properties
      var valueType = node.value.type;
      var valueIsMethod = node.method;
      if (valueType === 'FunctionExpression' && !valueIsMethod) {
        errors.add(
          'Property assignment should use enhanced object literal function. `{ funcName: function() {} }` is not allowed.',
          node.loc.start
        );
      }
    });
  }
};
<commit_msg>Add space in requireEnhancedObjectLiterals error<commit_after>var assert = require('assert');

module.exports = function() { };

module.exports.prototype = {
  configure: function(option) {
    assert(option === true, this.getOptionName() + ' requires a true value');
  },

  getOptionName: function() {
    return 'requireEnhancedObjectLiterals';
  },

  check: function(file, errors) {
    file.iterateNodesByType('Property', function(node) {
      // node.key.name is used when the property key is an unquoted identifier
      // node.key.value is used when the property key is a quoted string
      var propertyName = node.key.name || node.key.value;
      var valueName = node.value.name;
      var shorthand = node.shorthand;

      // check for non-shorthand properties
      if (propertyName && propertyName === valueName && !shorthand) {
        errors.add(
          'Property assignment should use enhanced object literal function. `{ propName: propName }` is not allowed.',
          node.loc.start
        );
      }

      // check for non-method function properties
      var valueType = node.value.type;
      var valueIsMethod = node.method;
      if (valueType === 'FunctionExpression' && !valueIsMethod) {
        errors.add(
          'Property assignment should use enhanced object literal function. `{ funcName: function() {} }` is not allowed.',
          node.loc.start
        );
      }
    });
  }
};
<|endoftext|>