<commit_before>import { createSelector, createStructuredSelector } from 'reselect';
import sortBy from 'lodash/sortBy';
import intersection from 'lodash/intersection';
import compact from 'lodash/compact';
import isEmpty from 'lodash/isEmpty';
import lowerCase from 'lodash/lowerCase';
import flatMap from 'lodash/flatMap';
import moment from 'moment';
import camelCase from 'lodash/camelCase';
import qs from 'query-string';
import { translateText } from 'utils/transifex';

import { getAllAreas } from 'providers/areas-provider/selectors';
import { getGeodescriberTitleFull } from 'providers/geodescriber-provider/selectors';
import { getActiveLayersWithDates } from 'components/map/selectors';
import { getDataLocation } from 'utils/location';

import { getIsTrase } from 'app/layouts/root/selectors';

import tropicalIsos from 'data/tropical-isos.json';
import colors from 'data/colors.json';
import { locationLevelToStr } from 'utils/format';

import {
  getSettingsConfig,
  getOptionsSelected,
  getIndicator,
  getStatements,
  getLocationPath
} from './utils/config';
import allWidgets from './manifest';

const buildLocationDict = locations =>
  location &&
  locations.length &&
  locations.reduce(
    (dict, next) => ({
      ...dict,
      [next.value || next.id]: {
        ...next
      }
    }),
    {}
  );

export const selectLocation = state => state.location && state.location.payload;
export const selectRouteType = state => state.location && state.location.type;
export const selectLocationQuery = state =>
  state.location && state.location.query;
export const selectLocationSearch = state =>
  state.location && state.location.search;
export const selectWidgetsData = state => state.widgets && state.widgets.data;
export const selectGeostore = state => state.geostore && state.geostore.data;
export const selectLoadingFilterData = state =>
  state.countryData &&
  state.whitelists &&
  state.areas &&
  (state.countryData.countriesLoading ||
    state.countryData.regionsLoading ||
    state.countryData.subRegionsLoading ||
    state.areas.loading ||
    state.whitelists.loading);
export const selectLoadingMeta = state =>
  state.geostore &&
  state.geodescriber &&
  (state.geostore.loading || state.geodescriber.loading);
export const selectCountryData = state => state.countryData;
export const selectPolynameWhitelist = state =>
  state.whitelists && state.whitelists.data;
export const selectEmbed = (state, { embed }) => embed;
export const selectSimple = (state, { simple }) => simple;
export const selectAnalysis = (state, { analysis }) => analysis;
export const selectCategory = state =>
  state.location && state.location.query && state.location.query.category;
export const selectModalClosing = state =>
  state.modalMeta && state.modalMeta.closing;
export const selectNonGlobalDatasets = state =>
  state.widgets && state.widgets.data.nonGlobalDatasets;

export const getWidgetFromLocation = createSelector(
  [selectLocation, selectLocationQuery],
  (location, query) =>
    (location && location.widgetSlug) || (query && query.widget)
);

export const getLocationObj = createSelector(
  [getDataLocation, getGeodescriberTitleFull],
  (location, title) => ({
    ...location,
    locationLabel: location.type === 'global' ? 'global' : title,
    adminLevel: locationLevelToStr(location),
    locationLabelFull: location.type === 'global' ? 'global' : title,
    isTropical: location && tropicalIsos.includes(location.adm0)
  })
);

export const getAllLocationData = createSelector(
  [
    getDataLocation,
    selectCountryData,
    getAllAreas,
    selectRouteType,
    selectLocationQuery
  ],
  (dataLocation, countryData, areas, routeType, query) => {
    if (isEmpty(areas) && isEmpty(countryData)) return null;
    const { type, adm0, adm1, areaId } = dataLocation;

    if (areaId && type !== 'country') {
      return { adm0: areas.map(a => ({ ...a, value: a.geostore })) };
    }

    if (type === 'global' || type === 'country') {
      return {
        adm0: countryData.countries.map(l => ({
          ...l,
          path: getLocationPath(routeType, type, query, { adm0: l.value })
        })),
        adm1: countryData.regions.map(l => ({
          ...l,
          path: getLocationPath(routeType, type, query, { adm0, adm1: l.value })
        })),
        adm2: countryData.subRegions.map(l => ({
          ...l,
          path: getLocationPath(routeType, type, query, {
            adm0,
            adm1,
            adm2: l.value
          })
        })),
        fao: countryData.faoCountries
      };
    }

    return {};
  }
);

export const getLocationData = createSelector(
  [getLocationObj, getAllLocationData, selectPolynameWhitelist],
  (locationObj, allLocationData, polynamesWhitelist) => {
    const { type, adminLevel, locationLabel, adm0, adm1, areaId } = locationObj;
    const { adm0: adm0Data, adm1: adm1Data, adm2: adm2Data } =
      allLocationData || {};

    let parent = {};
    let parentData = adm0Data;
    let children = adm0Data;
    if (adminLevel === 'adm0') {
      parent = { label: 'global', value: 'global' };
      children = adm1Data;
    } else if (adminLevel === 'adm1') {
      parent = adm0Data && adm0Data.find(d => d.value === adm0);
      parentData = adm0Data;
      children = adm2Data;
    } else if (adminLevel === 'adm2') {
      parent = adm1Data && adm1Data.find(d => d.value === adm1);
      parentData = adm1Data;
      children = [];
    }

    const locationData = allLocationData[adminLevel] || adm0Data;
    const currentLocation =
      locationData &&
      locationData.find(
        d => d.value === locationObj[adminLevel] || d.id === locationObj.areaId
      );

    const status = ['global', 'country', 'wdpa'].includes(locationObj.type)
      ? 'saved'
      : (currentLocation && currentLocation.status) || 'pending';

    return {
      parent,
      parentLabel: parent && parent.label,
      parentData: parentData && buildLocationDict(parentData),
      location: currentLocation || { label: 'global', value: 'global' },
      locationData: locationData && buildLocationDict(locationData),
      locationLabel:
        ['global', 'geostore', 'wdpa', 'use'].includes(type) || areaId
          ? locationLabel
          : currentLocation && currentLocation.label,
      childData: children && buildLocationDict(children),
      polynamesWhitelist,
      status
    };
  }
);

export const filterWidgetsByLocation = createSelector(
  [
    getLocationObj,
    getLocationData,
    selectPolynameWhitelist,
    selectEmbed,
    getWidgetFromLocation,
    getActiveLayersWithDates,
    selectAnalysis
  ],
  (
    location,
    locationData,
    polynameWhitelist,
    embed,
    widget,
    layers,
    showAnalysis
  ) => {
    const { adminLevel, type } = location;

    const widgets = Object.values(allWidgets).map(w => ({
      ...w,
      ...(w.colors && {
        colors: colors[w.colors]
      })
    }));

    if (embed && widget) return widgets.filter(w => w.widget === widget);
    const layerIds = layers && layers.map(l => l.id);

    return widgets.filter(w => {
      const {
        types,
        admins,
        whitelists,
        blacklists,
        source,
        datasets,
        visible
      } =
        w || {};
      const { fao, status } = locationData || {};

      const layerIntersection =
        datasets &&
        intersection(
          compact(
            flatMap(
              datasets.filter(d => !d.boundary).map(d => {
                const layersArray = Array.isArray(d.layers) && d.layers;

                return layersArray;
              })
            )
          ),
          layerIds
        );
      const hasLocation =
        types && types.includes(type) && admins && admins.includes(adminLevel);
      const adminWhitelist =
        type === 'country' && whitelists && whitelists.adm0;

      const adminBlacklist =
        type === 'country' && blacklists && blacklists[adminLevel];
      const notInBlacklist =
        !adminBlacklist || !adminBlacklist.includes(adminLevel);

      const isFAOCountry =
        source !== 'fao' || (fao && fao.find(f => f.value === location.adm0));
      const matchesAdminWhitelist =
        !adminWhitelist || adminWhitelist.includes(location.adm0);
      const polynameIntersection =
        whitelists &&
        whitelists.indicators &&
        intersection(
          polynameWhitelist[w.whitelistType || 'annual'],
          whitelists.indicators
        );
      const matchesPolynameWhitelist =
        !whitelists ||
        !whitelists.indicators ||
        (polynameIntersection && polynameIntersection.length);
      const isWidgetDataPending =
        !whitelists ||
        (status && status !== 'pending') ||
        !whitelists.checkStatus;

      const isWidgetVisible =
        (!showAnalysis && !visible) ||
        (showAnalysis &&
          visible &&
          visible.includes('analysis') &&
          !isEmpty(layerIntersection)) ||
        (!showAnalysis && visible && visible.includes('dashboard'));

      return (
        hasLocation &&
        matchesAdminWhitelist &&
        matchesPolynameWhitelist &&
        isFAOCountry &&
        isWidgetVisible &&
        notInBlacklist &&
        isWidgetDataPending
      );
    });
  }
);

export const getWidgetCategories = createSelector(
  [filterWidgetsByLocation],
  widgets => flatMap(widgets.map(w => w.categories))
);

export const getActiveCategory = createSelector(
  [selectCategory, getWidgetCategories],
  (activeCategory, widgetCats) => {
    if (!widgetCats) {
      return null;
    }

    return widgetCats.includes(activeCategory) ? activeCategory : 'summary';
  }
);

export const filterWidgetsByCategory = createSelector(
  [
    filterWidgetsByLocation,
    getActiveCategory,
    selectAnalysis,
    getLocationData,
    selectEmbed,
    getWidgetFromLocation
  ],
  (widgets, category, showAnalysis, locationData, embed, widget) => {
    if (isEmpty(widgets)) return null;

    if (embed && widget) return widgets.filter(w => w.widget === widget);

    if (showAnalysis) {
      return sortBy(widgets, 'sortOrder.summary');
    }

    return sortBy(
      widgets.filter(w => w.categories.includes(category)),
      `sortOrder[${camelCase(category)}]`
    );
  }
);

export const getWidgets = createSelector(
  [
    filterWidgetsByCategory,
    getLocationObj,
    getLocationData,
    selectWidgetsData,
    selectLocationQuery,
    selectLocationSearch,
    selectNonGlobalDatasets,
    getIsTrase,
    getActiveLayersWithDates,
    selectAnalysis
  ],
  (
    widgets,
    locationObj,
    locationData,
    widgetsData,
    query,
    search,
    datasets,
    isTrase,
    layers,
    analysis
  ) => {
    if (isEmpty(widgets) || !locationObj || !widgetsData) {
      return null;
    }

    const { locationLabelFull, type, adm0, adm1, adm2 } = locationObj || {};
    const { polynamesWhitelist, status } = locationData || {};

    return widgets.map(w => {
      const {
        settings: defaultSettings,
        widget,
        settingsConfig,
        pendingKeys,
        title: titleTemplate,
        dataType
      } =
        w || {};
      const rawData = widgetsData && widgetsData[widget];

      const { settings: dataSettings } = rawData || {};

      const widgetLayer =
        layers &&
        layers.find(
          l =>
            w.datasets && flatMap(w.datasets.map(d => d.layers)).includes(l.id)
        );

      const { params: layerParams, decodeParams } = widgetLayer || {};
      const startDate =
        (layerParams && layerParams.startDate) ||
        (decodeParams && decodeParams.startDate);
      const startYear =
        startDate && parseInt(moment(startDate).format('YYYY'), 10);
      const endDate =
        (layerParams && layerParams.endDate) ||
        (decodeParams && decodeParams.endDate);
      const endYear = endDate && parseInt(moment(endDate).format('YYYY'), 10);

      const widgetQuerySettings = query && query[widget];

      const layerSettings = {
        ...layerParams,
        ...decodeParams,
        ...(startYear && {
          startYear
        }),
        ...(endYear && {
          endYear
        })
      };

      const mergedSettings = {
        ...defaultSettings,
        ...dataSettings,
        ...widgetQuerySettings,
        ...(analysis && {
          ...layerSettings
        })
      };

      const settings = {
        ...mergedSettings,
        ...(mergedSettings.ifl === 2016 && {
          extentYear: 2010
        }),
        ...(mergedSettings.forestType === 'primary_forest' && {
          extentYear: 2000
        })
      };

      const dataOptions = rawData && rawData.options;

      const settingsConfigParsed = getSettingsConfig({
        settingsConfig,
        dataOptions,
        settings,
        polynamesWhitelist:
          polynamesWhitelist && polynamesWhitelist[w.whitelistType || 'annual'],
        status,
        pendingKeys
      });

      const optionsSelected =
        settingsConfigParsed && getOptionsSelected(settingsConfigParsed);

      const forestType = optionsSelected && optionsSelected.forestType;
      const landCategory = optionsSelected && optionsSelected.landCategory;
      const indicator = getIndicator(forestType, landCategory);

      const footerStatements = getStatements({
        forestType,
        landCategory,
        settings,
        datasets,
        type,
        dataType
      });

      const { ifl } = settings || {};

      const settingsConfigFiltered =
        settingsConfigParsed &&
        settingsConfigParsed.filter(
          o =>
            o.key !== 'extentYear' ||
            (ifl !== 2016 &&
              settings.forestType !== 'primary_forest' &&
              settings.forestType !== 'ifl')
        );

      const props = {
        ...w,
        ...locationObj,
        ...locationData,
        data: rawData,
        settings,
        title: titleTemplate,
        settingsConfig: settingsConfigFiltered,
        optionsSelected,
        indicator,
        showAttributionLink: isTrase,
        statements: footerStatements
      };

      const parsedProps = props.getWidgetProps && props.getWidgetProps(props);
      const { title: parsedTitle } = parsedProps || {};
      const title = parsedTitle || titleTemplate;

      const downloadLink =
        props.getDownloadLink &&
        props.getDownloadLink({ ...props, ...parsedProps });

      const searchObject = qs.parse(search);
      const widgetQuery = searchObject && searchObject[widget];
      const shareUrl = `${window.location.origin}${window.location.pathname}?${
        searchObject
          ? qs.stringify({
            ...searchObject,
            widget,
            showMap: false,
            scrollTo: widget
          })
          : ''
      }`;
      const embedUrl = `${window.location.origin}/embed/widget/${widget}/${
        type
      }${adm0 ? `/${adm0}` : ''}${adm1 ? `/${adm1}` : ''}${
        adm2 ? `/${adm2}` : ''
      }${widgetQuery ? `?${widget}=${widgetQuery}` : ''}`;

      return {
        ...props,
        ...parsedProps,
        shareUrl,
        embedUrl,
        downloadLink,
        rawData,
        title: title
          ? translateText(title).replace(
            '{location}',
            locationLabelFull || '...'
          )
          : ''
      };
    });
  }
);

export const getActiveWidget = createSelector(
  [getWidgets, getWidgetFromLocation, selectAnalysis],
  (widgets, activeWidgetKey, analysis) => {
    if (!widgets || analysis) return null;
    if (!activeWidgetKey) return widgets[0];
    return widgets.find(w => w.widget === activeWidgetKey);
  }
);

export const getNoDataMessage = createSelector(
  [getGeodescriberTitleFull, getActiveCategory],
  (title, category) => {
    if (!title || !category) return 'No data available';
    if (!category) return `No data available for ${title}`;
    return `No ${lowerCase(category)} data avilable for ${title}`;
  }
);

export const getWidgetsProps = () =>
  createStructuredSelector({
    loadingData: selectLoadingFilterData,
    loadingMeta: selectLoadingMeta,
    widgets: getWidgets,
    activeWidget: getActiveWidget,
    location: getDataLocation,
    emebd: selectEmbed,
    simple: selectSimple,
    modalClosing: selectModalClosing,
    noDataMessage: getNoDataMessage,
    geostore: selectGeostore
  });
<commit_msg>hotfix for finx wrong adm0 location name inside widget selectors<commit_after>import { createSelector, createStructuredSelector } from 'reselect';
import sortBy from 'lodash/sortBy';
import intersection from 'lodash/intersection';
import compact from 'lodash/compact';
import isEmpty from 'lodash/isEmpty';
import lowerCase from 'lodash/lowerCase';
import flatMap from 'lodash/flatMap';
import moment from 'moment';
import camelCase from 'lodash/camelCase';
import qs from 'query-string';
import { translateText } from 'utils/transifex';

import { getAllAreas } from 'providers/areas-provider/selectors';
import { getGeodescriberTitleFull } from 'providers/geodescriber-provider/selectors';
import { getActiveLayersWithDates } from 'components/map/selectors';
import { getDataLocation } from 'utils/location';

import { getIsTrase } from 'app/layouts/root/selectors';

import tropicalIsos from 'data/tropical-isos.json';
import colors from 'data/colors.json';
import { locationLevelToStr } from 'utils/format';

import {
  getSettingsConfig,
  getOptionsSelected,
  getIndicator,
  getStatements,
  getLocationPath
} from './utils/config';
import allWidgets from './manifest';

const buildLocationDict = locations =>
  location &&
  locations.length &&
  locations.reduce(
    (dict, next) => ({
      ...dict,
      [next.value || next.id]: {
        ...next
      }
    }),
    {}
  );

export const selectLocation = state => state.location && state.location.payload;
export const selectRouteType = state => state.location && state.location.type;
export const selectLocationQuery = state =>
  state.location && state.location.query;
export const selectLocationSearch = state =>
  state.location && state.location.search;
export const selectWidgetsData = state => state.widgets && state.widgets.data;
export const selectGeostore = state => state.geostore && state.geostore.data;
export const selectLoadingFilterData = state =>
  state.countryData &&
  state.whitelists &&
  state.areas &&
  (state.countryData.countriesLoading ||
    state.countryData.regionsLoading ||
    state.countryData.subRegionsLoading ||
    state.areas.loading ||
    state.whitelists.loading);
export const selectLoadingMeta = state =>
  state.geostore &&
  state.geodescriber &&
  (state.geostore.loading || state.geodescriber.loading);
export const selectCountryData = state => state.countryData;
export const selectPolynameWhitelist = state =>
  state.whitelists && state.whitelists.data;
export const selectEmbed = (state, { embed }) => embed;
export const selectSimple = (state, { simple }) => simple;
export const selectAnalysis = (state, { analysis }) => analysis;
export const selectCategory = state =>
  state.location && state.location.query && state.location.query.category;
export const selectModalClosing = state =>
  state.modalMeta && state.modalMeta.closing;
export const selectNonGlobalDatasets = state =>
  state.widgets && state.widgets.data.nonGlobalDatasets;

export const getWidgetFromLocation = createSelector(
  [selectLocation, selectLocationQuery],
  (location, query) =>
    (location && location.widgetSlug) || (query && query.widget)
);

export const getLocationObj = createSelector(
  [getDataLocation, getGeodescriberTitleFull],
  (location, title) => ({
    ...location,
    locationLabel: location.type === 'global' ? 'global' : title,
    adminLevel: locationLevelToStr(location),
    locationLabelFull: location.type === 'global' ? 'global' : title,
    isTropical: location && tropicalIsos.includes(location.adm0)
  })
);

export const getAllLocationData = createSelector(
  [
    getDataLocation,
    selectCountryData,
    getAllAreas,
    selectRouteType,
    selectLocationQuery
  ],
  (dataLocation, countryData, areas, routeType, query) => {
    if (isEmpty(areas) && isEmpty(countryData)) return null;
    const { type, adm0, adm1, areaId } = dataLocation;

    if (areaId && type !== 'country') {
      return { adm0: areas.map(a => ({ ...a, value: a.geostore })) };
    }

    if (type === 'global' || type === 'country') {
      return {
        adm0: countryData.countries.map(l => ({
          ...l,
          path: getLocationPath(routeType, type, query, { adm0: l.value })
        })),
        adm1: countryData.regions.map(l => ({
          ...l,
          path: getLocationPath(routeType, type, query, { adm0, adm1: l.value })
        })),
        adm2: countryData.subRegions.map(l => ({
          ...l,
          path: getLocationPath(routeType, type, query, {
            adm0,
            adm1,
            adm2: l.value
          })
        })),
        fao: countryData.faoCountries
      };
    }

    return {};
  }
);

export const getLocationData = createSelector(
  [getLocationObj, getAllLocationData, selectPolynameWhitelist],
  (locationObj, allLocationData, polynamesWhitelist) => {
    const { type, adminLevel, locationLabel, adm0, adm1, areaId } = locationObj;
    const { adm0: adm0Data, adm1: adm1Data, adm2: adm2Data } =
      allLocationData || {};

    let parent = {};
    let parentData = adm0Data;
    let children = adm0Data;
    if (adminLevel === 'adm0') {
      parent = { label: 'global', value: 'global' };
      children = adm1Data;
    } else if (adminLevel === 'adm1') {
      parent = adm0Data && adm0Data.find(d => d.value === adm0);
      parentData = adm0Data;
      children = adm2Data;
    } else if (adminLevel === 'adm2') {
      parent = adm1Data && adm1Data.find(d => d.value === adm1);
      parentData = adm1Data;
      children = [];
    }

    const locationData = allLocationData[adminLevel] || adm0Data;
    const currentLocation =
      locationData &&
      locationData.find(
        d =>
          d.value === locationObj[adminLevel] ||
          (d.id && d.id === locationObj.areaId)
      );

    const status = ['global', 'country', 'wdpa'].includes(locationObj.type)
      ? 'saved'
      : (currentLocation && currentLocation.status) || 'pending';

    return {
      parent,
      parentLabel: parent && parent.label,
      parentData: parentData && buildLocationDict(parentData),
      location: currentLocation || { label: 'global', value: 'global' },
      locationData: locationData && buildLocationDict(locationData),
      locationLabel:
        ['global', 'geostore', 'wdpa', 'use'].includes(type) || areaId
          ? locationLabel
          : currentLocation && currentLocation.label,
      childData: children && buildLocationDict(children),
      polynamesWhitelist,
      status
    };
  }
);

export const filterWidgetsByLocation = createSelector(
  [
    getLocationObj,
    getLocationData,
    selectPolynameWhitelist,
    selectEmbed,
    getWidgetFromLocation,
    getActiveLayersWithDates,
    selectAnalysis
  ],
  (
    location,
    locationData,
    polynameWhitelist,
    embed,
    widget,
    layers,
    showAnalysis
  ) => {
    const { adminLevel, type } = location;

    const widgets = Object.values(allWidgets).map(w => ({
      ...w,
      ...(w.colors && {
        colors: colors[w.colors]
      })
    }));

    if (embed && widget) return widgets.filter(w => w.widget === widget);
    const layerIds = layers && layers.map(l => l.id);

    return widgets.filter(w => {
      const {
        types,
        admins,
        whitelists,
        blacklists,
        source,
        datasets,
        visible
      } =
        w || {};
      const { fao, status } = locationData || {};

      const layerIntersection =
        datasets &&
        intersection(
          compact(
            flatMap(
              datasets.filter(d => !d.boundary).map(d => {
                const layersArray = Array.isArray(d.layers) && d.layers;

                return layersArray;
              })
            )
          ),
          layerIds
        );
      const hasLocation =
        types && types.includes(type) && admins && admins.includes(adminLevel);
      const adminWhitelist =
        type === 'country' && whitelists && whitelists.adm0;

      const adminBlacklist =
        type === 'country' && blacklists && blacklists[adminLevel];
      const notInBlacklist =
        !adminBlacklist || !adminBlacklist.includes(adminLevel);

      const isFAOCountry =
        source !== 'fao' || (fao && fao.find(f => f.value === location.adm0));
      const matchesAdminWhitelist =
        !adminWhitelist || adminWhitelist.includes(location.adm0);
      const polynameIntersection =
        whitelists &&
        whitelists.indicators &&
        intersection(
          polynameWhitelist[w.whitelistType || 'annual'],
          whitelists.indicators
        );
      const matchesPolynameWhitelist =
        !whitelists ||
        !whitelists.indicators ||
        (polynameIntersection && polynameIntersection.length);
      const isWidgetDataPending =
        !whitelists ||
        (status && status !== 'pending') ||
        !whitelists.checkStatus;

      const isWidgetVisible =
        (!showAnalysis && !visible) ||
        (showAnalysis &&
          visible &&
          visible.includes('analysis') &&
          !isEmpty(layerIntersection)) ||
        (!showAnalysis && visible && visible.includes('dashboard'));

      return (
        hasLocation &&
        matchesAdminWhitelist &&
        matchesPolynameWhitelist &&
        isFAOCountry &&
        isWidgetVisible &&
        notInBlacklist &&
        isWidgetDataPending
      );
    });
  }
);

export const getWidgetCategories = createSelector(
  [filterWidgetsByLocation],
  widgets => flatMap(widgets.map(w => w.categories))
);

export const getActiveCategory = createSelector(
  [selectCategory, getWidgetCategories],
  (activeCategory, widgetCats) => {
    if (!widgetCats) {
      return null;
    }

    return widgetCats.includes(activeCategory) ? activeCategory : 'summary';
  }
);

export const filterWidgetsByCategory = createSelector(
  [
    filterWidgetsByLocation,
    getActiveCategory,
    selectAnalysis,
    getLocationData,
    selectEmbed,
    getWidgetFromLocation
  ],
  (widgets, category, showAnalysis, locationData, embed, widget) => {
    if (isEmpty(widgets)) return null;

    if (embed && widget) return widgets.filter(w => w.widget === widget);

    if (showAnalysis) {
      return sortBy(widgets, 'sortOrder.summary');
    }

    return sortBy(
      widgets.filter(w => w.categories.includes(category)),
      `sortOrder[${camelCase(category)}]`
    );
  }
);

export const getWidgets = createSelector(
  [
    filterWidgetsByCategory,
    getLocationObj,
    getLocationData,
    selectWidgetsData,
    selectLocationQuery,
    selectLocationSearch,
    selectNonGlobalDatasets,
    getIsTrase,
    getActiveLayersWithDates,
    selectAnalysis
  ],
  (
    widgets,
    locationObj,
    locationData,
    widgetsData,
    query,
    search,
    datasets,
    isTrase,
    layers,
    analysis
  ) => {
    if (isEmpty(widgets) || !locationObj || !widgetsData) {
      return null;
    }

    const { locationLabelFull, type, adm0, adm1, adm2 } = locationObj || {};
    const { polynamesWhitelist, status } = locationData || {};

    return widgets.map(w => {
      const {
        settings: defaultSettings,
        widget,
        settingsConfig,
        pendingKeys,
        title: titleTemplate,
        dataType
      } =
        w || {};
      const rawData = widgetsData && widgetsData[widget];

      const { settings: dataSettings } = rawData || {};

      const widgetLayer =
        layers &&
        layers.find(
          l =>
            w.datasets && flatMap(w.datasets.map(d => d.layers)).includes(l.id)
        );

      const { params: layerParams, decodeParams } = widgetLayer || {};
      const startDate =
        (layerParams && layerParams.startDate) ||
        (decodeParams && decodeParams.startDate);
      const startYear =
        startDate && parseInt(moment(startDate).format('YYYY'), 10);
      const endDate =
        (layerParams && layerParams.endDate) ||
        (decodeParams && decodeParams.endDate);
      const endYear = endDate && parseInt(moment(endDate).format('YYYY'), 10);

      const widgetQuerySettings = query && query[widget];

      const layerSettings = {
        ...layerParams,
        ...decodeParams,
        ...(startYear && {
          startYear
        }),
        ...(endYear && {
          endYear
        })
      };

      const mergedSettings = {
        ...defaultSettings,
        ...dataSettings,
        ...widgetQuerySettings,
        ...(analysis && {
          ...layerSettings
        })
      };

      const settings = {
        ...mergedSettings,
        ...(mergedSettings.ifl === 2016 && {
          extentYear: 2010
        }),
        ...(mergedSettings.forestType === 'primary_forest' && {
          extentYear: 2000
        })
      };

      const dataOptions = rawData && rawData.options;

      const settingsConfigParsed = getSettingsConfig({
        settingsConfig,
        dataOptions,
        settings,
        polynamesWhitelist:
          polynamesWhitelist && polynamesWhitelist[w.whitelistType || 'annual'],
        status,
        pendingKeys
      });

      const optionsSelected =
        settingsConfigParsed && getOptionsSelected(settingsConfigParsed);

      const forestType = optionsSelected && optionsSelected.forestType;
      const landCategory = optionsSelected && optionsSelected.landCategory;
      const indicator = getIndicator(forestType, landCategory);

      const footerStatements = getStatements({
        forestType,
        landCategory,
        settings,
        datasets,
        type,
        dataType
      });

      const { ifl } = settings || {};

      const settingsConfigFiltered =
        settingsConfigParsed &&
        settingsConfigParsed.filter(
          o =>
            o.key !== 'extentYear' ||
            (ifl !== 2016 &&
              settings.forestType !== 'primary_forest' &&
              settings.forestType !== 'ifl')
        );

      const props = {
        ...w,
        ...locationObj,
        ...locationData,
        data: rawData,
        settings,
        title: titleTemplate,
        settingsConfig: settingsConfigFiltered,
        optionsSelected,
        indicator,
        showAttributionLink: isTrase,
        statements: footerStatements
      };

      const parsedProps = props.getWidgetProps && props.getWidgetProps(props);
      const { title: parsedTitle } = parsedProps || {};
      const title = parsedTitle || titleTemplate;

      const downloadLink =
        props.getDownloadLink &&
        props.getDownloadLink({ ...props, ...parsedProps });

      const searchObject = qs.parse(search);
      const widgetQuery = searchObject && searchObject[widget];
      const shareUrl = `${window.location.origin}${window.location.pathname}?${
        searchObject
          ? qs.stringify({
            ...searchObject,
            widget,
            showMap: false,
            scrollTo: widget
          })
          : ''
      }`;
      const embedUrl = `${window.location.origin}/embed/widget/${widget}/${
        type
      }${adm0 ? `/${adm0}` : ''}${adm1 ? `/${adm1}` : ''}${
        adm2 ? `/${adm2}` : ''
      }${widgetQuery ? `?${widget}=${widgetQuery}` : ''}`;

      return {
        ...props,
        ...parsedProps,
        shareUrl,
        embedUrl,
        downloadLink,
        rawData,
        title: title
          ? translateText(title).replace(
            '{location}',
            locationLabelFull || '...'
          )
          : ''
      };
    });
  }
);

export const getActiveWidget = createSelector(
  [getWidgets, getWidgetFromLocation, selectAnalysis],
  (widgets, activeWidgetKey, analysis) => {
    if (!widgets || analysis) return null;
    if (!activeWidgetKey) return widgets[0];
    return widgets.find(w => w.widget === activeWidgetKey);
  }
);

export const getNoDataMessage = createSelector(
  [getGeodescriberTitleFull, getActiveCategory],
  (title, category) => {
    if (!title || !category) return 'No data available';
    if (!category) return `No data available for ${title}`;
    return `No ${lowerCase(category)} data avilable for ${title}`;
  }
);

export const getWidgetsProps = () =>
  createStructuredSelector({
    loadingData: selectLoadingFilterData,
    loadingMeta: selectLoadingMeta,
    widgets: getWidgets,
    activeWidget: getActiveWidget,
    location: getDataLocation,
    emebd: selectEmbed,
    simple: selectSimple,
    modalClosing: selectModalClosing,
    noDataMessage: getNoDataMessage,
    geostore: selectGeostore
  });
<|endoftext|>


<commit_before>//----------------------------------------------------
// Button
//----------------------------------------------------

import React from "react"
import { withKnobs, boolean, radios } from "@storybook/addon-knobs"

import copyCodeBlock from "@pickra/copy-code-block"

//import prettier from "prettier/standalone"
//import parserHtml from "prettier/parser-html"
import beautify from "js-beautify"
import hljs from "highlight.js/lib/highlight"
hljs.registerLanguage("html", require("highlight.js/lib/languages/xml"))

export default {
  title: "Components | Modules / Button",
  decorators: [withKnobs]
}

const styles = {
  Plain: "is-plain",
  Outline: "is-outline",
  Melt: "is-melt"
}

const items = [
  { id: 0, role: "", text: "戻る", icon: "angle-left" },
  { id: 1, role: "is-primary", text: "決定", icon: "check" },
  { id: 2, role: "is-info", text: "情報", icon: "info-circle" },
  { id: 3, role: "is-success", text: "登録", icon: "save" },
  { id: 4, role: "is-warning", text: "注意", icon: "exclamation-triangle" },
  { id: 5, role: "is-danger", text: "削除", icon: "times" }
]

const beautifyHtmlOptions = {
  inline: ["span", "i"],
  indent_size: 2
}

//----------------------------------------------------
// / Basic
//----------------------------------------------------

export const basic = () => {
  const style = radios("Style", styles, "is-plain")
  const strong = boolean("Strong", false) ? "is-strong" : ""
  const round = boolean("Round", false) ? "is-round" : ""
  const floating = boolean("Floating", false) ? "is-floating" : ""
  const disabled = boolean("Disabled", false) ? "disabled" : ""
  const contentsView = items.map(item => (
    <button
      key={item.id}
      className={["button", style, item.role, strong, round, floating]
        .join(" ")
        .replace(/\s+/g, " ")
        .replace(/^\s+|\s+$/g, "")}
      type="button"
      disabled={disabled}
    >
      {item.text}
    </button>
  ))
  const contentsCode = items
    .map(
      item =>
        `<button
          class="button ${style} ${item.role} ${strong} ${round} ${floating}"
          type="button"
          ${disabled}
        >${item.text}</button>`
    )
    .join("")
    .replace(/\s+/g, " ")
    .replace(/\s\"/g, '"')
  /*const formattedCode = prettier.format(contentsCode, {
    parser: "html",
    plugins: [parserHtml],
    htmlWhitespaceSensitivity: "ignore"
  })*/
  const formattedCode = beautify.html(contentsCode, beautifyHtmlOptions)
  const highlightCode = copyCodeBlock(formattedCode, { lang: "html" })
  return (

        {contentsView}

  )
}
basic.story = { name: "Basic" }

//----------------------------------------------------
// / Square
//----------------------------------------------------

export const square = () => {
  const style = radios("Style", styles, "is-plain")
  const floating = boolean("Floating", false) ? "is-floating" : ""
  const disabled = boolean("Disabled", false) ? "disabled" : ""
  const contentsView = items.map(item => (
    <button
      key={item.id}
      className={["button", style, "is-square", item.role, floating]
        .join(" ")
        .replace(/\s+/g, " ")
        .replace(/^\s+|\s+$/g, "")}
      type="button"
      disabled={disabled}
    >

    </button>
  ))
  const contentsCode = items
    .map(
      item =>
        `<button
          class="button ${style} is-square ${item.role} ${floating}"
          type="button"
          ${disabled}
        ></button>`
    )
    .join("")
    .replace(/\s+/g, " ")
    .replace(/\s\"/g, '"')
  const formattedCode = beautify.html(contentsCode, beautifyHtmlOptions)
  const highlightCode = copyCodeBlock(formattedCode, { lang: "html" })
  return (

        {contentsView}

  )
}
square.story = { name: "Square" }

//----------------------------------------------------
// / Circle
//----------------------------------------------------

export const circle = () => {
  const style = radios("Style", styles, "is-plain")
  const floating = boolean("Floating", false) ? "is-floating" : ""
  const disabled = boolean("Disabled", false) ? "disabled" : ""
  const contentsView = items.map(item => (
    <button
      key={item.id}
      className={["button", style, "is-circle", item.role, floating]
        .join(" ")
        .replace(/\s+/g, " ")
        .replace(/^\s+|\s+$/g, "")}
      type="button"
      disabled={disabled}
    >

    </button>
  ))
  const contentsCode = items
    .map(
      item =>
        `<button
          class="button ${style} is-circle ${item.role} ${floating}"
          type="button"
          ${disabled}
        ></button>`
    )
    .join("")
    .replace(/\s+/g, " ")
    .replace(/\s\"/g, '"')
  const formattedCode = beautify.html(contentsCode, beautifyHtmlOptions)
  const highlightCode = copyCodeBlock(formattedCode, { lang: "html" })
  return (

        {contentsView}

  )
}
circle.story = { name: "Circle" }
<commit_msg>Add demo button angle<commit_after>//----------------------------------------------------
// Button
//----------------------------------------------------

import React from "react"
import { withKnobs, boolean, radios } from "@storybook/addon-knobs"

import copyCodeBlock from "@pickra/copy-code-block"

//import prettier from "prettier/standalone"
//import parserHtml from "prettier/parser-html"
import beautify from "js-beautify"
import hljs from "highlight.js/lib/highlight"
hljs.registerLanguage("html", require("highlight.js/lib/languages/xml"))

export default {
  title: "Styles | Components / Button",
  decorators: [withKnobs]
}

const items = [
  { id: 0, role: "", text: "戻る", icon: "angle-left" },
  { id: 1, role: "is-primary", text: "決定", icon: "check" },
  { id: 2, role: "is-info", text: "情報", icon: "info-circle" },
  { id: 3, role: "is-success", text: "登録", icon: "save" },
  { id: 4, role: "is-warning", text: "注意", icon: "exclamation-triangle" },
  { id: 5, role: "is-danger", text: "削除", icon: "times" }
]

const styles = {
  Plain: "is-plain",
  Outline: "is-outline",
  Melt: "is-melt"
}

const angles = {
  None: "",
  Right: "is-angle-right",
  Left: "is-angle-left"
}

const angleVs = {
  Default: "",
  Up: "is-angle-up",
  Down: "is-angle-down"
}

const beautifyHtmlOptions = {
  inline: ["span", "i"],
  indent_size: 2
}

//----------------------------------------------------
// / Basic
//----------------------------------------------------

export const basic = () => {
  const style = radios("Style", styles, "is-plain")
  const angleH = radios("Angle", angles, "")
  const anglesV = radios("Angle+", angleVs, "")
  const strong = boolean("Strong", false) ? "is-strong" : ""
  const round = boolean("Round", false) ? "is-round" : ""
  const floating = boolean("Floating", false) ? "is-floating" : ""
  const disabled = boolean("Disabled", false) ? "disabled" : ""
  const contents = items
    .map(
      item =>
        `<button
          class="button ${style} ${item.role} ${angleH} ${anglesV} ${strong} ${round} ${floating}"
          type="button"
          ${disabled}
        >${item.text}</button>`
    )
    .join("")
    .replace(/\s+/g, " ")
    .replace(/\s\"/g, '"')
  /*const formattedCode = prettier.format(contentsCode, {
    parser: "html",
    plugins: [parserHtml],
    htmlWhitespaceSensitivity: "ignore"
  })*/
  const formatted = beautify.html(contents, beautifyHtmlOptions)
  const highlighted = copyCodeBlock(formatted, { lang: "html" })
  return (

  )
}
basic.story = { name: "Basic" }

//----------------------------------------------------
// / Square
//----------------------------------------------------

export const square = () => {
  const style = radios("Style", styles, "is-plain")
  const floating = boolean("Floating", false) ? "is-floating" : ""
  const disabled = boolean("Disabled", false) ? "disabled" : ""
  const contents = items
    .map(
      item =>
        `<button
          class="button ${style} is-square ${item.role} ${floating}"
          type="button"
          ${disabled}
        ></button>`
    )
    .join("")
    .replace(/\s+/g, " ")
    .replace(/\s\"/g, '"')
  const formatted = beautify.html(contents, beautifyHtmlOptions)
  const highlighted = copyCodeBlock(formatted, { lang: "html" })
  return (

  )
}
square.story = { name: "Square" }

//----------------------------------------------------
// / Circle
//----------------------------------------------------

export const circle = () => {
  const style = radios("Style", styles, "is-plain")
  const floating = boolean("Floating", false) ? "is-floating" : ""
  const disabled = boolean("Disabled", false) ? "disabled" : ""
  const contents = items
    .map(
      item =>
        `<button
          class="button ${style} is-circle ${item.role} ${floating}"
          type="button"
          ${disabled}
        ></button>`
    )
    .join("")
    .replace(/\s+/g, " ")
    .replace(/\s\"/g, '"')
  const formatted = beautify.html(contents, beautifyHtmlOptions)
  const highlighted = copyCodeBlock(formatted, { lang: "html" })
  return (

  )
}
circle.story = { name: "Circle" }
<|endoftext|>


<commit_before>/**
 * node-compress-commons
 *
 * Copyright (c) 2014 Chris Talkington, contributors.
 * Licensed under the MIT license.
 * https://github.com/archiverjs/node-compress-commons/blob/master/LICENSE-MIT
 */
var inherits = require('util').inherits;
var crc32 = require('buffer-crc32');
var CRC32Stream = require('crc32-stream');
var DeflateCRC32Stream = CRC32Stream.DeflateCRC32Stream;

var ArchiveOutputStream = require('../archive-output-stream');
var ZipArchiveEntry = require('./zip-archive-entry');
var GeneralPurposeBit = require('./general-purpose-bit');

var constants = require('./constants');
var util = require('../../util');
var zipUtil = require('./util');

var ZipArchiveOutputStream = module.exports = function(options) {
  if (!(this instanceof ZipArchiveOutputStream)) {
    return new ZipArchiveOutputStream(options);
  }

  options = this.options = this._defaults(options);

  ArchiveOutputStream.call(this, options);

  this._entry = null;
  this._entries = [];
  this._archive = {
    centralLength: 0,
    centralOffset: 0,
    comment: '',
    finish: false,
    finished: false,
    processing: false,
    forceZip64: options.forceZip64,
    forceLocalTime: options.forceLocalTime
  };
};

inherits(ZipArchiveOutputStream, ArchiveOutputStream);

ZipArchiveOutputStream.prototype._afterAppend = function(ae) {
  this._entries.push(ae);

  if (ae.getGeneralPurposeBit().usesDataDescriptor()) {
    this._writeDataDescriptor(ae);
  }

  this._archive.processing = false;
  this._entry = null;

  if (this._archive.finish && !this._archive.finished) {
    this._finish();
  }
};

ZipArchiveOutputStream.prototype._appendBuffer = function(ae, source, callback) {
  if (source.length === 0) {
    ae.setMethod(constants.METHOD_STORED);
  }

  var method = ae.getMethod();

  if (method === constants.METHOD_STORED) {
    ae.setSize(source.length);
    ae.setCompressedSize(source.length);
    ae.setCrc(crc32.unsigned(source));
  }

  this._writeLocalFileHeader(ae);

  if (method === constants.METHOD_STORED) {
    this.write(source);
    this._afterAppend(ae);
    callback(null, ae);
    return;
  } else if (method === constants.METHOD_DEFLATED) {
    this._smartStream(ae, callback).end(source);
    return;
  } else {
    callback(new Error('compression method ' + method + ' not implemented'));
    return;
  }
};

ZipArchiveOutputStream.prototype._appendStream = function(ae, source, callback) {
  ae.getGeneralPurposeBit().useDataDescriptor(true);
  ae.setVersionNeededToExtract(constants.MIN_VERSION_DATA_DESCRIPTOR);

  this._writeLocalFileHeader(ae);

  var smart = this._smartStream(ae, callback);
  source.once('error', function(err) {
    smart.emit('error', err);
    smart.end();
  })
  source.pipe(smart);
};

ZipArchiveOutputStream.prototype._defaults = function(o) {
  if (typeof o !== 'object') {
    o = {};
  }

  if (typeof o.zlib !== 'object') {
    o.zlib = {};
  }

  if (typeof o.zlib.level !== 'number') {
    o.zlib.level = constants.ZLIB_BEST_SPEED;
  }

  o.forceZip64 = !!o.forceZip64;
  o.forceLocalTime = !!o.forceLocalTime;

  return o;
};

ZipArchiveOutputStream.prototype._finish = function() {
  this._archive.centralOffset = this.offset;

  this._entries.forEach(function(ae) {
    this._writeCentralFileHeader(ae);
  }.bind(this));

  this._archive.centralLength = this.offset - this._archive.centralOffset;

  if (this.isZip64()) {
    this._writeCentralDirectoryZip64();
  }

  this._writeCentralDirectoryEnd();

  this._archive.processing = false;
  this._archive.finish = true;
  this._archive.finished = true;
  this.end();
};

ZipArchiveOutputStream.prototype._normalizeEntry = function(ae) {
  if (ae.getMethod() === -1) {
    ae.setMethod(constants.METHOD_DEFLATED);
  }

  if (ae.getMethod() === constants.METHOD_DEFLATED) {
    ae.getGeneralPurposeBit().useDataDescriptor(true);
    ae.setVersionNeededToExtract(constants.MIN_VERSION_DATA_DESCRIPTOR);
  }

  if (ae.getTime() === -1) {
    ae.setTime(new Date(), this._archive.forceLocalTime);
  }

  ae._offsets = {
    file: 0,
    data: 0,
    contents: 0,
  };
};

ZipArchiveOutputStream.prototype._smartStream = function(ae, callback) {
  var deflate = ae.getMethod() === constants.METHOD_DEFLATED;
  var process = deflate ? new DeflateCRC32Stream(this.options.zlib) : new CRC32Stream();
  var error = null;

  function handleStuff() {
    var digest = process.digest().readUInt32BE(0);
    ae.setCrc(digest);
    ae.setSize(process.size());
    ae.setCompressedSize(process.size(true));
    this._afterAppend(ae);
    callback(error, ae);
  }

  process.once('end', handleStuff.bind(this));
  process.once('error', function(err) {
    error = err;
  });

  process.pipe(this, { end: false });

  return process;
};

ZipArchiveOutputStream.prototype._writeCentralDirectoryEnd = function() {
  var records = this._entries.length;
  var size = this._archive.centralLength;
  var offset = this._archive.centralOffset;

  if (this.isZip64()) {
    records = constants.ZIP64_MAGIC_SHORT;
    size = constants.ZIP64_MAGIC;
    offset = constants.ZIP64_MAGIC;
  }

  // signature
  this.write(zipUtil.getLongBytes(constants.SIG_EOCD));

  // disk numbers
  this.write(constants.SHORT_ZERO);
  this.write(constants.SHORT_ZERO);

  // number of entries
  this.write(zipUtil.getShortBytes(records));
  this.write(zipUtil.getShortBytes(records));

  // length and location of CD
  this.write(zipUtil.getLongBytes(size));
  this.write(zipUtil.getLongBytes(offset));

  // archive comment
  var comment = this.getComment();
  var commentLength = Buffer.byteLength(comment);
  this.write(zipUtil.getShortBytes(commentLength));
  this.write(comment);
};

ZipArchiveOutputStream.prototype._writeCentralDirectoryZip64 = function() {
  // signature
  this.write(zipUtil.getLongBytes(constants.SIG_ZIP64_EOCD));

  // size of the ZIP64 EOCD record
  this.write(zipUtil.getEightBytes(56));

  // version made by
  this.write(zipUtil.getShortBytes(constants.MIN_VERSION_ZIP64));

  // version to extract
  this.write(zipUtil.getShortBytes(constants.MIN_VERSION_ZIP64));

  // disk numbers
  this.write(constants.LONG_ZERO);
  this.write(constants.LONG_ZERO);

  // number of entries
  this.write(zipUtil.getEightBytes(this._entries.length));
  this.write(zipUtil.getEightBytes(this._entries.length));

  // length and location of CD
  this.write(zipUtil.getEightBytes(this._archive.centralLength));
  this.write(zipUtil.getEightBytes(this._archive.centralOffset));

  // extensible data sector
  // not implemented at this time

  // end of central directory locator
  this.write(zipUtil.getLongBytes(constants.SIG_ZIP64_EOCD_LOC));

  // disk number holding the ZIP64 EOCD record
  this.write(constants.LONG_ZERO);

  // relative offset of the ZIP64 EOCD record
  this.write(zipUtil.getEightBytes(this._archive.centralOffset + this._archive.centralLength));

  // total number of disks
  this.write(zipUtil.getLongBytes(1));
};

ZipArchiveOutputStream.prototype._writeCentralFileHeader = function(ae) {
  var gpb = ae.getGeneralPurposeBit();
  var method = ae.getMethod();
  var offsets = ae._offsets;

  var size = ae.getSize();
  var compressedSize = ae.getCompressedSize();

  if (ae.isZip64() || offsets.file > constants.ZIP64_MAGIC) {
    size = constants.ZIP64_MAGIC;
    compressedSize = constants.ZIP64_MAGIC;

    ae.setVersionNeededToExtract(constants.MIN_VERSION_ZIP64);

    var extraBuf = Buffer.concat([
      zipUtil.getShortBytes(constants.ZIP64_EXTRA_ID),
      zipUtil.getShortBytes(24),
      zipUtil.getEightBytes(ae.getSize()),
      zipUtil.getEightBytes(ae.getCompressedSize()),
      zipUtil.getEightBytes(offsets.file)
    ], 28);

    ae.setExtra(extraBuf);
  }

  // signature
  this.write(zipUtil.getLongBytes(constants.SIG_CFH));

  // version made by
  this.write(zipUtil.getShortBytes((ae.getPlatform() << 8) | constants.VERSION_MADEBY));

  // version to extract and general bit flag
  this.write(zipUtil.getShortBytes(ae.getVersionNeededToExtract()));
  this.write(gpb.encode());

  // compression method
  this.write(zipUtil.getShortBytes(method));

  // datetime
  this.write(zipUtil.getLongBytes(ae.getTimeDos()));

  // crc32 checksum
  this.write(zipUtil.getLongBytes(ae.getCrc()));

  // sizes
  this.write(zipUtil.getLongBytes(compressedSize));
  this.write(zipUtil.getLongBytes(size));

  var name = ae.getName();
  var comment = ae.getComment();
  var extra = ae.getCentralDirectoryExtra();

  if (gpb.usesUTF8ForNames()) {
    name = new Buffer(name);
    comment = new Buffer(comment);
  }

  // name length
  this.write(zipUtil.getShortBytes(name.length));

  // extra length
  this.write(zipUtil.getShortBytes(extra.length));

  // comments length
  this.write(zipUtil.getShortBytes(comment.length));

  // disk number start
  this.write(constants.SHORT_ZERO);

  // internal attributes
  this.write(zipUtil.getShortBytes(ae.getInternalAttributes()));

  // external attributes
  this.write(zipUtil.getLongBytes(ae.getExternalAttributes()));

  // relative offset of LFH
  if (offsets.file > constants.ZIP64_MAGIC) {
    this.write(zipUtil.getLongBytes(constants.ZIP64_MAGIC));
  } else {
    this.write(zipUtil.getLongBytes(offsets.file));
  }

  // name
  this.write(name);

  // extra
  this.write(extra);

  // comment
  this.write(comment);
};

ZipArchiveOutputStream.prototype._writeDataDescriptor = function(ae) {
  // signature
  this.write(zipUtil.getLongBytes(constants.SIG_DD));

  // crc32 checksum
  this.write(zipUtil.getLongBytes(ae.getCrc()));

  // sizes
  if (ae.isZip64()) {
    this.write(zipUtil.getEightBytes(ae.getCompressedSize()));
    this.write(zipUtil.getEightBytes(ae.getSize()));
  } else {
    this.write(zipUtil.getLongBytes(ae.getCompressedSize()));
    this.write(zipUtil.getLongBytes(ae.getSize()));
  }
};

ZipArchiveOutputStream.prototype._writeLocalFileHeader = function(ae) {
  var gpb = ae.getGeneralPurposeBit();
  var method = ae.getMethod();
  var name = ae.getName();
  var extra = ae.getLocalFileDataExtra();

  if (ae.isZip64()) {
    gpb.useDataDescriptor(true);
    ae.setVersionNeededToExtract(constants.MIN_VERSION_ZIP64);
  }

  if (gpb.usesUTF8ForNames()) {
    name = new Buffer(name);
  }

  ae._offsets.file = this.offset;

  // signature
  this.write(zipUtil.getLongBytes(constants.SIG_LFH));

  // version to extract and general bit flag
  this.write(zipUtil.getShortBytes(ae.getVersionNeededToExtract()));
  this.write(gpb.encode());

  // compression method
  this.write(zipUtil.getShortBytes(method));

  // datetime
  this.write(zipUtil.getLongBytes(ae.getTimeDos()));

  ae._offsets.data = this.offset;

  // crc32 checksum and sizes
  if (gpb.usesDataDescriptor()) {
    this.write(constants.LONG_ZERO);
    this.write(constants.LONG_ZERO);
    this.write(constants.LONG_ZERO);
  } else {
    this.write(zipUtil.getLongBytes(ae.getCrc()));
    this.write(zipUtil.getLongBytes(ae.getCompressedSize()));
    this.write(zipUtil.getLongBytes(ae.getSize()));
  }

  // name length
  this.write(zipUtil.getShortBytes(name.length));

  // extra length
  this.write(zipUtil.getShortBytes(extra.length));

  // name
  this.write(name);

  // extra
  this.write(extra);

  ae._offsets.contents = this.offset;
};

ZipArchiveOutputStream.prototype.getComment = function(comment) {
  return this._archive.comment !== null ? this._archive.comment : '';
};

ZipArchiveOutputStream.prototype.isZip64 = function() {
  return this._archive.forceZip64 || this._entries.length > constants.ZIP64_MAGIC_SHORT || this._archive.centralLength > constants.ZIP64_MAGIC || this._archive.centralOffset > constants.ZIP64_MAGIC;
};

ZipArchiveOutputStream.prototype.setComment = function(comment) {
  this._archive.comment = comment;
};<commit_msg>Fix ZIP64 directory header size<commit_after>/**
 * node-compress-commons
 *
 * Copyright (c) 2014 Chris Talkington, contributors.
 * Licensed under the MIT license.
 * https://github.com/archiverjs/node-compress-commons/blob/master/LICENSE-MIT
 */
var inherits = require('util').inherits;
var crc32 = require('buffer-crc32');
var CRC32Stream = require('crc32-stream');
var DeflateCRC32Stream = CRC32Stream.DeflateCRC32Stream;

var ArchiveOutputStream = require('../archive-output-stream');
var ZipArchiveEntry = require('./zip-archive-entry');
var GeneralPurposeBit = require('./general-purpose-bit');

var constants = require('./constants');
var util = require('../../util');
var zipUtil = require('./util');

var ZipArchiveOutputStream = module.exports = function(options) {
  if (!(this instanceof ZipArchiveOutputStream)) {
    return new ZipArchiveOutputStream(options);
  }

  options = this.options = this._defaults(options);

  ArchiveOutputStream.call(this, options);

  this._entry = null;
  this._entries = [];
  this._archive = {
    centralLength: 0,
    centralOffset: 0,
    comment: '',
    finish: false,
    finished: false,
    processing: false,
    forceZip64: options.forceZip64,
    forceLocalTime: options.forceLocalTime
  };
};

inherits(ZipArchiveOutputStream, ArchiveOutputStream);

ZipArchiveOutputStream.prototype._afterAppend = function(ae) {
  this._entries.push(ae);

  if (ae.getGeneralPurposeBit().usesDataDescriptor()) {
    this._writeDataDescriptor(ae);
  }

  this._archive.processing = false;
  this._entry = null;

  if (this._archive.finish && !this._archive.finished) {
    this._finish();
  }
};

ZipArchiveOutputStream.prototype._appendBuffer = function(ae, source, callback) {
  if (source.length === 0) {
    ae.setMethod(constants.METHOD_STORED);
  }

  var method = ae.getMethod();

  if (method === constants.METHOD_STORED) {
    ae.setSize(source.length);
    ae.setCompressedSize(source.length);
    ae.setCrc(crc32.unsigned(source));
  }

  this._writeLocalFileHeader(ae);

  if (method === constants.METHOD_STORED) {
    this.write(source);
    this._afterAppend(ae);
    callback(null, ae);
    return;
  } else if (method === constants.METHOD_DEFLATED) {
    this._smartStream(ae, callback).end(source);
    return;
  } else {
    callback(new Error('compression method ' + method + ' not implemented'));
    return;
  }
};

ZipArchiveOutputStream.prototype._appendStream = function(ae, source, callback) {
  ae.getGeneralPurposeBit().useDataDescriptor(true);
  ae.setVersionNeededToExtract(constants.MIN_VERSION_DATA_DESCRIPTOR);

  this._writeLocalFileHeader(ae);

  var smart = this._smartStream(ae, callback);
  source.once('error', function(err) {
    smart.emit('error', err);
    smart.end();
  })
  source.pipe(smart);
};

ZipArchiveOutputStream.prototype._defaults = function(o) {
  if (typeof o !== 'object') {
    o = {};
  }

  if (typeof o.zlib !== 'object') {
    o.zlib = {};
  }

  if (typeof o.zlib.level !== 'number') {
    o.zlib.level = constants.ZLIB_BEST_SPEED;
  }

  o.forceZip64 = !!o.forceZip64;
  o.forceLocalTime = !!o.forceLocalTime;

  return o;
};

ZipArchiveOutputStream.prototype._finish = function() {
  this._archive.centralOffset = this.offset;

  this._entries.forEach(function(ae) {
    this._writeCentralFileHeader(ae);
  }.bind(this));

  this._archive.centralLength = this.offset - this._archive.centralOffset;

  if (this.isZip64()) {
    this._writeCentralDirectoryZip64();
  }

  this._writeCentralDirectoryEnd();

  this._archive.processing = false;
  this._archive.finish = true;
  this._archive.finished = true;
  this.end();
};

ZipArchiveOutputStream.prototype._normalizeEntry = function(ae) {
  if (ae.getMethod() === -1) {
    ae.setMethod(constants.METHOD_DEFLATED);
  }

  if (ae.getMethod() === constants.METHOD_DEFLATED) {
    ae.getGeneralPurposeBit().useDataDescriptor(true);
    ae.setVersionNeededToExtract(constants.MIN_VERSION_DATA_DESCRIPTOR);
  }

  if (ae.getTime() === -1) {
    ae.setTime(new Date(), this._archive.forceLocalTime);
  }

  ae._offsets = {
    file: 0,
    data: 0,
    contents: 0,
  };
};

ZipArchiveOutputStream.prototype._smartStream = function(ae, callback) {
  var deflate = ae.getMethod() === constants.METHOD_DEFLATED;
  var process = deflate ? new DeflateCRC32Stream(this.options.zlib) : new CRC32Stream();
  var error = null;

  function handleStuff() {
    var digest = process.digest().readUInt32BE(0);
    ae.setCrc(digest);
    ae.setSize(process.size());
    ae.setCompressedSize(process.size(true));
    this._afterAppend(ae);
    callback(error, ae);
  }

  process.once('end', handleStuff.bind(this));
  process.once('error', function(err) {
    error = err;
  });

  process.pipe(this, { end: false });

  return process;
};

ZipArchiveOutputStream.prototype._writeCentralDirectoryEnd = function() {
  var records = this._entries.length;
  var size = this._archive.centralLength;
  var offset = this._archive.centralOffset;

  if (this.isZip64()) {
    records = constants.ZIP64_MAGIC_SHORT;
    size = constants.ZIP64_MAGIC;
    offset = constants.ZIP64_MAGIC;
  }

  // signature
  this.write(zipUtil.getLongBytes(constants.SIG_EOCD));

  // disk numbers
  this.write(constants.SHORT_ZERO);
  this.write(constants.SHORT_ZERO);

  // number of entries
  this.write(zipUtil.getShortBytes(records));
  this.write(zipUtil.getShortBytes(records));

  // length and location of CD
  this.write(zipUtil.getLongBytes(size));
  this.write(zipUtil.getLongBytes(offset));

  // archive comment
  var comment = this.getComment();
  var commentLength = Buffer.byteLength(comment);
  this.write(zipUtil.getShortBytes(commentLength));
  this.write(comment);
};

ZipArchiveOutputStream.prototype._writeCentralDirectoryZip64 = function() {
  // signature
  this.write(zipUtil.getLongBytes(constants.SIG_ZIP64_EOCD));

  // size of the ZIP64 EOCD record
  this.write(zipUtil.getEightBytes(44));

  // version made by
  this.write(zipUtil.getShortBytes(constants.MIN_VERSION_ZIP64));

  // version to extract
  this.write(zipUtil.getShortBytes(constants.MIN_VERSION_ZIP64));

  // disk numbers
  this.write(constants.LONG_ZERO);
  this.write(constants.LONG_ZERO);

  // number of entries
  this.write(zipUtil.getEightBytes(this._entries.length));
  this.write(zipUtil.getEightBytes(this._entries.length));

  // length and location of CD
  this.write(zipUtil.getEightBytes(this._archive.centralLength));
  this.write(zipUtil.getEightBytes(this._archive.centralOffset));

  // extensible data sector
  // not implemented at this time

  // end of central directory locator
  this.write(zipUtil.getLongBytes(constants.SIG_ZIP64_EOCD_LOC));

  // disk number holding the ZIP64 EOCD record
  this.write(constants.LONG_ZERO);

  // relative offset of the ZIP64 EOCD record
  this.write(zipUtil.getEightBytes(this._archive.centralOffset + this._archive.centralLength));

  // total number of disks
  this.write(zipUtil.getLongBytes(1));
};

ZipArchiveOutputStream.prototype._writeCentralFileHeader = function(ae) {
  var gpb = ae.getGeneralPurposeBit();
  var method = ae.getMethod();
  var offsets = ae._offsets;

  var size = ae.getSize();
  var compressedSize = ae.getCompressedSize();

  if (ae.isZip64() || offsets.file > constants.ZIP64_MAGIC) {
    size = constants.ZIP64_MAGIC;
    compressedSize = constants.ZIP64_MAGIC;

    ae.setVersionNeededToExtract(constants.MIN_VERSION_ZIP64);

    var extraBuf = Buffer.concat([
      zipUtil.getShortBytes(constants.ZIP64_EXTRA_ID),
      zipUtil.getShortBytes(24),
      zipUtil.getEightBytes(ae.getSize()),
      zipUtil.getEightBytes(ae.getCompressedSize()),
      zipUtil.getEightBytes(offsets.file)
    ], 28);

    ae.setExtra(extraBuf);
  }

  // signature
  this.write(zipUtil.getLongBytes(constants.SIG_CFH));

  // version made by
  this.write(zipUtil.getShortBytes((ae.getPlatform() << 8) | constants.VERSION_MADEBY));

  // version to extract and general bit flag
  this.write(zipUtil.getShortBytes(ae.getVersionNeededToExtract()));
  this.write(gpb.encode());

  // compression method
  this.write(zipUtil.getShortBytes(method));

  // datetime
  this.write(zipUtil.getLongBytes(ae.getTimeDos()));

  // crc32 checksum
  this.write(zipUtil.getLongBytes(ae.getCrc()));

  // sizes
  this.write(zipUtil.getLongBytes(compressedSize));
  this.write(zipUtil.getLongBytes(size));

  var name = ae.getName();
  var comment = ae.getComment();
  var extra = ae.getCentralDirectoryExtra();

  if (gpb.usesUTF8ForNames()) {
    name = new Buffer(name);
    comment = new Buffer(comment);
  }

  // name length
  this.write(zipUtil.getShortBytes(name.length));

  // extra length
  this.write(zipUtil.getShortBytes(extra.length));

  // comments length
  this.write(zipUtil.getShortBytes(comment.length));

  // disk number start
  this.write(constants.SHORT_ZERO);

  // internal attributes
  this.write(zipUtil.getShortBytes(ae.getInternalAttributes()));

  // external attributes
  this.write(zipUtil.getLongBytes(ae.getExternalAttributes()));

  // relative offset of LFH
  if (offsets.file > constants.ZIP64_MAGIC) {
    this.write(zipUtil.getLongBytes(constants.ZIP64_MAGIC));
  } else {
    this.write(zipUtil.getLongBytes(offsets.file));
  }

  // name
  this.write(name);

  // extra
  this.write(extra);

  // comment
  this.write(comment);
};

ZipArchiveOutputStream.prototype._writeDataDescriptor = function(ae) {
  // signature
  this.write(zipUtil.getLongBytes(constants.SIG_DD));

  // crc32 checksum
  this.write(zipUtil.getLongBytes(ae.getCrc()));

  // sizes
  if (ae.isZip64()) {
    this.write(zipUtil.getEightBytes(ae.getCompressedSize()));
    this.write(zipUtil.getEightBytes(ae.getSize()));
  } else {
    this.write(zipUtil.getLongBytes(ae.getCompressedSize()));
    this.write(zipUtil.getLongBytes(ae.getSize()));
  }
};

ZipArchiveOutputStream.prototype._writeLocalFileHeader = function(ae) {
  var gpb = ae.getGeneralPurposeBit();
  var method = ae.getMethod();
  var name = ae.getName();
  var extra = ae.getLocalFileDataExtra();

  if (ae.isZip64()) {
    gpb.useDataDescriptor(true);
    ae.setVersionNeededToExtract(constants.MIN_VERSION_ZIP64);
  }

  if (gpb.usesUTF8ForNames()) {
    name = new Buffer(name);
  }

  ae._offsets.file = this.offset;

  // signature
  this.write(zipUtil.getLongBytes(constants.SIG_LFH));

  // version to extract and general bit flag
  this.write(zipUtil.getShortBytes(ae.getVersionNeededToExtract()));
  this.write(gpb.encode());

  // compression method
  this.write(zipUtil.getShortBytes(method));

  // datetime
  this.write(zipUtil.getLongBytes(ae.getTimeDos()));

  ae._offsets.data = this.offset;

  // crc32 checksum and sizes
  if (gpb.usesDataDescriptor()) {
    this.write(constants.LONG_ZERO);
    this.write(constants.LONG_ZERO);
    this.write(constants.LONG_ZERO);
  } else {
    this.write(zipUtil.getLongBytes(ae.getCrc()));
    this.write(zipUtil.getLongBytes(ae.getCompressedSize()));
    this.write(zipUtil.getLongBytes(ae.getSize()));
  }

  // name length
  this.write(zipUtil.getShortBytes(name.length));

  // extra length
  this.write(zipUtil.getShortBytes(extra.length));

  // name
  this.write(name);

  // extra
  this.write(extra);

  ae._offsets.contents = this.offset;
};

ZipArchiveOutputStream.prototype.getComment = function(comment) {
  return this._archive.comment !== null ? this._archive.comment : '';
};

ZipArchiveOutputStream.prototype.isZip64 = function() {
  return this._archive.forceZip64 || this._entries.length > constants.ZIP64_MAGIC_SHORT || this._archive.centralLength > constants.ZIP64_MAGIC || this._archive.centralOffset > constants.ZIP64_MAGIC;
};

ZipArchiveOutputStream.prototype.setComment = function(comment) {
  this._archive.comment = comment;
};
<|endoftext|>


<commit_before>import fs from 'fs';
import path from 'path';
import { spawn } from 'child_process';

import { exec } from 'mz/child_process';
import eventToPromise from 'event-to-promise';

import API from './api';

const SILK_SCRIPT_BUILD = 'silk-build';

let _pkgRoot = null;
function findPackageRoot() {
  if (_pkgRoot) {
    return _pkgRoot;
  }

  let curPath = process.cwd();
  let curName = path.join(curPath, 'package.json');

  while (!fs.existsSync(curName)) {
    if (curPath === '/') {
      throw new Error(`Could not locate 'package.json' file. Ensure you're running this command inside a Silk extension project.`);
    }
    curPath = path.dirname(curPath);
    curName = path.join(curPath, 'package.json');
  }

  _pkgRoot = curPath;
  return _pkgRoot;
}

function sourceArg() {
  return [['--source'], {
    help: `Source of the files `,
    defaultValue: findPackageRoot(),
    type: (value) => path.resolve(value),
  }];
}

function printDevices(devices) {
  for (let device of devices) {
    console.log(`${device.serial}\t(state = ${device.state})`);
  }
}

async function checkDevices(api, device) {
  const devices = await api.listDevices();
  if (!devices.length) {
    console.error(`No devices online`);
    process.exit(1);
  }

  if (!device && devices.length > 1) {
    console.error(`More than one device connected. Please pass --device\n`);
    console.error(`Use one of the following devices:`);
    printDevices(devices);
    process.exit(1);
  }
}

/**
 * This function is run from every cli call to ensure the enviornment is
 * correctly setup.
 */
function ensureSetup(argv) {
  const root = findPackageRoot();
  const emulatorBin = path.join(root, 'node_modules/silk-sdk-emulator/vendor/bin');

  // Additional paths to search for outside of PATH.
  const additionalPaths = [];

  if (fs.existsSync(emulatorBin)) {
    additionalPaths.push(emulatorBin);
  }

  argv.arguments = argv.arguments || [];
  argv.arguments = [
    [['--device', '-d'], {
      help: 'Specific device to operate under.'
    }],
    ...argv.arguments,
  ];

  const main = argv.main;
  argv.main = function (args) {
    const {device} = args;
    const api = new API({
      device: device,
      additionalPaths: additionalPaths,
    });

    return main(api, args);
  };

  return argv;
}

async function runBuild(pkg, cmd, dest) {
  const buildEnv = {};
  for (let key in process.env) {
    buildEnv[key] = process.env[key];
  }

  buildEnv.SILK_OUT = dest;
  // TODO: This would be good place to boostrap the system to handle things like
  // building native modules.

  console.log(' > silk-build: ', cmd);
  const proc = spawn('/bin/bash', ['-c', cmd], {
    env: buildEnv,
    stdio: 'inherit',
  });

  const exitCode = await eventToPromise(proc, 'exit');
  if (exitCode !== 0) {
    console.error(`Your project has failed to build.`);
    process.exit(1);
  }
}

async function silkBuild(pkg, dest) {
  if (pkg.scripts && pkg.scripts[SILK_SCRIPT_BUILD]) {
    await runBuild(pkg, pkg.scripts[SILK_SCRIPT_BUILD]);
  }
}

export let devices = ensureSetup({
  help: `List the devices available (NOTE: This includes non silk devices).`,
  main: async (api, args) => {
    const devices = await api.listDevices();
    if (devices.length === 0) {
      console.log(`No devices available`);
      return;
    }

    console.log('Available devices:\n');
    printDevices(devices);
  },
});

/**
 * Run and tail the program.
 */
export let run = ensureSetup({
  help: 'Run package on device as the main module.',
  arguments: [
    sourceArg(),
  ],
  main: async (api, args) => {
    await checkDevices(api, args.device);
    const pkg = require(path.join(findPackageRoot(), 'package.json'));
    await silkBuild(pkg, args.source);
    await api.pushModule(pkg.name, args.source);
    await api.activate(pkg.name);
    await api.restart();
    const tags = [
      'node:*',         // console.log()
      'silk-init:*',
      'silk-camera:*',
      'silk-wifi:*',
      'silk-ntp:*',
    ];
    const logcat = api.logcat(['-T1', '-v', 'time', '-s', ...tags]);
    logcat.stdout.pipe(process.stdout);
    logcat.stderr.pipe(process.stdout);
  },
});

export let build = ensureSetup({
  help: `Run the silk build script without pushing`,
  arguments: [
    sourceArg(),
  ],
  main: async (api, args) => {
    const pkg = require(path.join(findPackageRoot(), 'package.json'));
    await silkBuild(pkg, args.source);
  }
});

export let push = ensureSetup({
  help: `Push files without restarting the main module`,
  arguments: [
    sourceArg(),
  ],
  main: async (api, args) => {
    await checkDevices(api, args.device);
    const pkg = require(path.join(findPackageRoot(), 'package.json'));
    await api.pushModule(pkg.name, args.source);
  }
});

export let activate = ensureSetup({
  help: `Activate the current package as main module`,
  arguments: [
    [['--clear', '-c'], {
      action: 'storeTrue',
      help: `Activate the default main module instead of the current package`
    }]
  ],
  main: async (api, args) => {
    await checkDevices(api, args.device);
    let main = null;
    if (!args.clear) {
      const pkg = require(path.join(findPackageRoot(), 'package.json'));
      main = pkg.name;
    }
    await api.activate(main);
    if (!main) {
      main = '(device default)';
    }
    console.log(`Activated main program: ${main}`);
  }
});

export let restart = ensureSetup({
  help: `Restart the main module`,
  main: async (api, args) => {
    await api.restart();
  }
});

export let start = ensureSetup({
  help: `Start the main module (only useful after using stop)`,
  main: async (api, args) => {
    await api.start();
  }
});

export let stop = ensureSetup({
  help: `Stop the main module (until the device reboots)`,
  main: async (api, args) => {
    await api.stop();
  }
});

export let log = ensureSetup({
  help: `Begin tailing log of silk device or emulator`,
  arguments: [
    [['--recent', '-r'], {
      action: 'storeTrue',
      help: `Start tailing from current place in log`
    }]
  ],
  main: async (api, args) => {
    const logcatArgs = [];
    if (args.recent) {
      logcatArgs.push('-T1');
    }

    const logcat = api.logcat(logcatArgs);
    logcat.stdout.pipe(process.stdout);
    logcat.stderr.pipe(process.stdout);
  }
});

export let setupwifi = ensureSetup({
  help: `Configure wifi on the device or emulator`,
  arguments: [
    [['ssid'], {
      help: 'Wi-Fi network ssid to configure to',
    }],
    [['password'], {
      help: 'Wi-Fi network password credential (supply empty string for open networks)',
    }],
  ],
  main: async (api, args) => {
    await api.setupWifi(args.ssid, args.password);
  }
});
<commit_msg>Set default |silk build| output to .silkslug rather than .<commit_after>import fs from 'fs';
import path from 'path';
import { spawn } from 'child_process';

import { exec } from 'mz/child_process';
import eventToPromise from 'event-to-promise';

import API from './api';

const SILK_SCRIPT_BUILD = 'silk-build';

let _pkgRoot = null;
function findPackageRoot() {
  if (_pkgRoot) {
    return _pkgRoot;
  }

  let curPath = process.cwd();
  let curName = path.join(curPath, 'package.json');

  while (!fs.existsSync(curName)) {
    if (curPath === '/') {
      throw new Error(`Could not locate 'package.json' file. Ensure you're running this command inside a Silk extension project.`);
    }
    curPath = path.dirname(curPath);
    curName = path.join(curPath, 'package.json');
  }

  _pkgRoot = curPath;
  return _pkgRoot;
}

function sourceArgDefaultValue() {
  const dir = findPackageRoot();
  const pkg = require(path.join(dir, 'package.json'));
  if (pkg.scripts && pkg.scripts[SILK_SCRIPT_BUILD]) {
    return path.join(dir, '.silkslug');
  }
  return dir;
}

function sourceArg() {
  return [['--source'], {
    help: `Source of the files `,
    defaultValue: sourceArgDefaultValue(),
    type: (value) => path.resolve(value),
  }];
}

function printDevices(devices) {
  for (let device of devices) {
    console.log(`${device.serial}\t(state = ${device.state})`);
  }
}

async function checkDevices(api, device) {
  const devices = await api.listDevices();
  if (!devices.length) {
    console.error(`No devices online`);
    process.exit(1);
  }

  if (!device && devices.length > 1) {
    console.error(`More than one device connected. Please pass --device\n`);
    console.error(`Use one of the following devices:`);
    printDevices(devices);
    process.exit(1);
  }
}

/**
 * This function is run from every cli call to ensure the enviornment is
 * correctly setup.
 */
function ensureSetup(argv) {
  const root = findPackageRoot();
  const emulatorBin = path.join(root, 'node_modules/silk-sdk-emulator/vendor/bin');

  // Additional paths to search for outside of PATH.
  const additionalPaths = [];

  if (fs.existsSync(emulatorBin)) {
    additionalPaths.push(emulatorBin);
  }

  argv.arguments = argv.arguments || [];
  argv.arguments = [
    [['--device', '-d'], {
      help: 'Specific device to operate under.'
    }],
    ...argv.arguments,
  ];

  const main = argv.main;
  argv.main = function (args) {
    const {device} = args;
    const api = new API({
      device: device,
      additionalPaths: additionalPaths,
    });

    return main(api, args);
  };

  return argv;
}

async function runBuild(pkg, cmd, dest) {
  const buildEnv = {};
  for (let key in process.env) {
    buildEnv[key] = process.env[key];
  }

  buildEnv.SILK_OUT = dest;
  // TODO: This would be good place to bootstrap the system to handle things like
  // building native modules.

  console.log(' > silk-build: ', cmd);
  const proc = spawn('/bin/bash', ['-c', cmd], {
    env: buildEnv,
    stdio: 'inherit',
  });

  const exitCode = await eventToPromise(proc, 'exit');
  if (exitCode !== 0) {
    console.error(`Your project has failed to build.`);
    process.exit(1);
  }
}

async function silkBuild(pkg, dest) {
  if (pkg.scripts && pkg.scripts[SILK_SCRIPT_BUILD]) {
    await runBuild(pkg, pkg.scripts[SILK_SCRIPT_BUILD], dest);
  }
}

export let devices = ensureSetup({
  help: `List the devices available (NOTE: This includes non silk devices).`,
  main: async (api, args) => {
    const devices = await api.listDevices();
    if (devices.length === 0) {
      console.log(`No devices available`);
      return;
    }

    console.log('Available devices:\n');
    printDevices(devices);
  },
});

/**
 * Run and tail the program.
 */
export let run = ensureSetup({
  help: 'Run package on device as the main module.',
  arguments: [
    sourceArg(),
  ],
  main: async (api, args) => {
    await checkDevices(api, args.device);
    const pkg = require(path.join(findPackageRoot(), 'package.json'));
    await silkBuild(pkg, args.source);
    await api.pushModule(pkg.name, args.source);
    await api.activate(pkg.name);
    await api.restart();
    const tags = [
      'node:*',         // console.log()
      'silk-init:*',
      'silk-camera:*',
      'silk-wifi:*',
      'silk-ntp:*',
    ];
    const logcat = api.logcat(['-T1', '-v', 'time', '-s', ...tags]);
    logcat.stdout.pipe(process.stdout);
    logcat.stderr.pipe(process.stdout);
  },
});

export let build = ensureSetup({
  help: `Run the silk build script without pushing`,
  arguments: [
    sourceArg(),
  ],
  main: async (api, args) => {
    const pkg = require(path.join(findPackageRoot(), 'package.json'));
    await silkBuild(pkg, args.source);
  }
});

export let push = ensureSetup({
  help: `Push files without restarting the main module`,
  arguments: [
    sourceArg(),
  ],
  main: async (api, args) => {
    await checkDevices(api, args.device);
    const pkg = require(path.join(findPackageRoot(), 'package.json'));
    await api.pushModule(pkg.name, args.source);
  }
});

export let activate = ensureSetup({
  help: `Activate the current package as main module`,
  arguments: [
    [['--clear', '-c'], {
      action: 'storeTrue',
      help: `Activate the default main module instead of the current package`
    }]
  ],
  main: async (api, args) => {
    await checkDevices(api, args.device);
    let main = null;
    if (!args.clear) {
      const pkg = require(path.join(findPackageRoot(), 'package.json'));
      main = pkg.name;
    }
    await api.activate(main);
    if (!main) {
      main = '(device default)';
    }
    console.log(`Activated main program: ${main}`);
  }
});

export let restart = ensureSetup({
  help: `Restart the main module`,
  main: async (api, args) => {
    await api.restart();
  }
});

export let start = ensureSetup({
  help: `Start the main module (only useful after using stop)`,
  main: async (api, args) => {
    await api.start();
  }
});

export let stop = ensureSetup({
  help: `Stop the main module (until the device reboots)`,
  main: async (api, args) => {
    await api.stop();
  }
});

export let log = ensureSetup({
  help: `Begin tailing log of silk device or emulator`,
  arguments: [
    [['--recent', '-r'], {
      action: 'storeTrue',
      help: `Start tailing from current place in log`
    }]
  ],
  main: async (api, args) => {
    const logcatArgs = [];
    if (args.recent) {
      logcatArgs.push('-T1');
    }

    const logcat = api.logcat(logcatArgs);
    logcat.stdout.pipe(process.stdout);
    logcat.stderr.pipe(process.stdout);
  }
});

export let setupwifi = ensureSetup({
  help: `Configure wifi on the device or emulator`,
  arguments: [
    [['ssid'], {
      help: 'Wi-Fi network ssid to configure to',
    }],
    [['password'], {
      help: 'Wi-Fi network password credential (supply empty string for open networks)',
    }],
  ],
  main: async (api, args) => {
    await api.setupWifi(args.ssid, args.password);
  }
});
<|endoftext|>


<commit_before>AutoForm.addInputType("typeahead", {
  template: "afTypeahead",
  valueOut: function () {
    return this.typeahead('val');
  },
  valueConverters: {
    "number": AutoForm.Utility.stringToNumber,
    "numberArray": function (val) {
      if (_.isArray(val)) {
        return _.map(val, function (item) {
          item = $.trim(item);
          return AutoForm.Utility.stringToNumber(item);
        });
      }
      return val;
    },
    "boolean": AutoForm.Utility.stringToBool,
    "booleanArray": function (val) {
      if (_.isArray(val)) {
        return _.map(val, function (item) {
          item = $.trim(item);
          return AutoForm.Utility.stringToBool(item);
        });
      }
      return val;
    },
    "date": AutoForm.Utility.stringToDate,
    "dateArray": function (val) {
      if (_.isArray(val)) {
        return _.map(val, function (item) {
          item = $.trim(item);
          return AutoForm.Utility.stringToDate(item);
        });
      }
      return val;
    }
  }
});

Template.afTypeahead.helpers({
  atts: function () {
    var atts = _.clone(this.atts);
    atts = AutoForm.Utility.addClass(atts, "twitter-typeahead form-control");
    delete atts.typeaheadOptions;
    delete atts.typeaheadDatasets;
    return atts;
  }
});

Template.afTypeahead.rendered = function () {
  // instanciate typeahead
  var substringMatcher = function(strs) {
    return function findMatches(q, cb) {
      var matches, substrRegex;
      // an array that will be populated with substring matches
      matches = [];
      // regex used to determine if a string contains the substring `q`
      substrRegex = new RegExp(q, 'i');
      // iterate through the pool of strings and for any string that
      // contains the substring `q`, add it to the `matches` array
      $.each(strs, function(i, str) {
        if (substrRegex.test(str.value)) {
          // the typeahead jQuery plugin expects suggestions to a
          // JavaScript object, refer to typeahead docs for more info
          matches.push({ value: str.value });
        }
      });
      cb(matches);
    };
  };
  var options = {
    highlight: true
  };
  if (this.data.atts.typeaheadOptions) {
    _.extend(options, this.data.atts.typeaheadOptions);
  }
  var datasets = {
    source: substringMatcher(this.data.selectOptions)
  };
  if (this.data.atts.typeaheadDatasets) {
    _.extend(datasets, this.data.atts.typeaheadDatasets);
  }
  this.$('.twitter-typeahead').typeahead(options, datasets);
};

Template.afTypeahead.destroyed = function () {
  $('.twitter-typeahead').typeahead('destroy');
};
<commit_msg>FIXED label instead value for autoform.options<commit_after>AutoForm.addInputType("typeahead", {
  template: "afTypeahead",
  valueOut: function () {
    return this.typeahead('val');
  },
  valueConverters: {
    "number": AutoForm.Utility.stringToNumber,
    "numberArray": function (val) {
      if (_.isArray(val)) {
        return _.map(val, function (item) {
          item = $.trim(item);
          return AutoForm.Utility.stringToNumber(item);
        });
      }
      return val;
    },
    "boolean": AutoForm.Utility.stringToBool,
    "booleanArray": function (val) {
      if (_.isArray(val)) {
        return _.map(val, function (item) {
          item = $.trim(item);
          return AutoForm.Utility.stringToBool(item);
        });
      }
      return val;
    },
    "date": AutoForm.Utility.stringToDate,
    "dateArray": function (val) {
      if (_.isArray(val)) {
        return _.map(val, function (item) {
          item = $.trim(item);
          return AutoForm.Utility.stringToDate(item);
        });
      }
      return val;
    }
  }
});

Template.afTypeahead.helpers({
  atts: function () {
    var atts = _.clone(this.atts);
    atts = AutoForm.Utility.addClass(atts, "twitter-typeahead form-control");
    delete atts.typeaheadOptions;
    delete atts.typeaheadDatasets;
    return atts;
  }
});

Template.afTypeahead.rendered = function () {
  // instanciate typeahead
  var substringMatcher = function(strs) {
    return function findMatches(q, cb) {
      var matches, substrRegex;
      // an array that will be populated with substring matches
      matches = [];
      // regex used to determine if a string contains the substring `q`
      substrRegex = new RegExp(q, 'i');
      // iterate through the pool of strings and for any string that
      // contains the substring `q`, add it to the `matches` array
      $.each(strs, function(i, str) {
        if (substrRegex.test(str.label)) {
          // the typeahead jQuery plugin expects suggestions to a
          // JavaScript object, refer to typeahead docs for more info
          matches.push({ value: str.label });
        }
      });
      cb(matches);
    };
  };
  var options = {
    highlight: true
  };
  if (this.data.atts.typeaheadOptions) {
    _.extend(options, this.data.atts.typeaheadOptions);
  }
  var datasets = {
    source: substringMatcher(this.data.selectOptions)
  };
  if (this.data.atts.typeaheadDatasets) {
    _.extend(datasets, this.data.atts.typeaheadDatasets);
  }
  this.$('.twitter-typeahead').typeahead(options, datasets);
};

Template.afTypeahead.destroyed = function () {
  $('.twitter-typeahead').typeahead('destroy');
};
<|endoftext|>


<commit_before>$.fn.selectRange = function (options) {

    var $settings = $.extend({min: 0, max: 20}, options),
        $options = '',
        $val = this.val();

    this.empty();
    for(i = $settings.min; i <= $settings.max; i++) {
        $options += '<option value="' + i + '">' + i + '</option>';
    }
    return this.html($options).val($val);
};
<commit_msg>Update selectRange.js<commit_after>$.fn.selectRange = function (options) {

    var $settings = $.extend({min: 0, max: 20}, options),
        $options = '',
        $val = this.val();

    this.empty();
    for(i = $settings.min; i <= $settings.max; i++) {
        $options += '<option value="' + i + '"' + (i == $val ? ' selected="selected"' : '') + '>' + i + '</option>';
    }
    return this.html($options);
};
<|endoftext|>


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

  var availity = root.availity;

  var AnalyticsSplunkServiceFactory = function($log, avLogMessagesResource) {

    var SplunkAnalyticService = function() {};
    var proto = SplunkAnalyticService.prototype;

    proto.trackEvent = function(properties) {
      return avLogMessagesResource[properties.level](properties);
    };

    proto.trackPageView  = function(url) {
      $log.log('URL visited', url);
    };

    return new SplunkAnalyticService();
  };

  availity.core.factory('analyticsSplunkService', AnalyticsSplunkServiceFactory);

})(window);
<commit_msg>changed splunk service name<commit_after>(function(root) {
  'use strict';

  var availity = root.availity;

  var AnalyticsSplunkServiceFactory = function($log, avLogMessagesResource) {

    var SplunkAnalyticService = function() {};
    var proto = SplunkAnalyticService.prototype;

    proto.trackEvent = function(properties) {
      return avLogMessagesResource[properties.level](properties);
    };

    proto.trackPageView  = function(url) {
      $log.log('URL visited', url);
    };

    return new SplunkAnalyticService();
  };

  availity.core.factory('SplunkAnalyticsService', AnalyticsSplunkServiceFactory);

})(window);
<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var http = require('http');
var livereload = require('connect-livereload');
var mix = require('mix');
var morgan = require('morgan');
var parseurl = require('parseurl');
var tinylr = require('tiny-lr');

module.exports = function (port, behavior) {
    var tinylrServer = tinylr();
    var app = express()
        // .use(morgan())
        .use(livereload({ port: port }))
        .use(staticMiddleware);
    if (behavior) {
        app = behavior(app, getNodeData);
    }
    app
        .use(fileMiddleware('index.html'))
        .use(tinylrServer.handler.bind(tinylrServer));
    var server = http.createServer(app);
    server.on('upgrade', tinylrServer.websocketify.bind(tinylrServer));
    server.listen(port);

    var currentTree = new mix.Tree([]);

    function staticMiddleware(req, res, next) {
        var pathname = parseurl(req).pathname;
        return fileMiddleware(pathname.substr(1))(req, res, next);
    }

    function fileMiddleware(name) {
        return function (req, res, next) {
            var node = currentTree.findNodeByName(name);
            if (node !== null && (req.method === 'GET' || req.method === 'HEAD')) {
                var data = node.data;
                res.writeHead(200, {
                    'Content-Length': data.length,
                    'Content-Type': node.metadata.mime
                });
                if (req.method === 'GET') {
                    res.end(data);
                } else {
                    res.end();
                }
            } else {
                next();
            }
        }
    }

    function getNodeData(name, callback) {
        var node = currentTree.findNodeByName(name);
        if (node !== null) {
            callback(null, node.data);
        } else {
            callback(new Error('not-found'));
        }
    }

    return function (tree) {
        currentTree = tree;
        tinylr.changed('/not-used-anyway');
        return tree;
    };
};
<commit_msg>Move livereload serving to its default port<commit_after>'use strict';

var express = require('express');
var http = require('http');
var livereload = require('connect-livereload');
var mix = require('mix');
var morgan = require('morgan');
var parseurl = require('parseurl');
var tinylr = require('tiny-lr');

var LIVERELOAD_PORT = 35729;

module.exports = function (port, behavior) {
    var tinylrServer = tinylr();
    tinylrServer.listen(LIVERELOAD_PORT);

    var app = express()
        // .use(morgan())
        .use(livereload({ port: LIVERELOAD_PORT }))
        .use(staticMiddleware);
    if (behavior) {
        app = behavior(app, getNodeData);
    }
    app.use(fileMiddleware('index.html'));
    app.listen(port);

    var currentTree = new mix.Tree([]);

    function staticMiddleware(req, res, next) {
        var pathname = parseurl(req).pathname;
        return fileMiddleware(pathname.substr(1))(req, res, next);
    }

    function fileMiddleware(name) {
        return function (req, res, next) {
            var node = currentTree.findNodeByName(name);
            if (node !== null && (req.method === 'GET' || req.method === 'HEAD')) {
                var data = node.data;
                res.writeHead(200, {
                    'Content-Length': data.length,
                    'Content-Type': node.metadata.mime
                });
                if (req.method === 'GET') {
                    res.end(data);
                } else {
                    res.end();
                }
            } else {
                next();
            }
        }
    }

    function getNodeData(name, callback) {
        var node = currentTree.findNodeByName(name);
        if (node !== null) {
            callback(null, node.data);
        } else {
            callback(new Error('not-found'));
        }
    }

    return function (tree) {
        currentTree = tree;
        tree.nodes.forEach(function (node) {
            tinylr.changed('/' + node.name);
        });
        return tree;
    };
};
<|endoftext|>


<commit_before>/*!
 * Backbone.js ajaxRetry - v1.0.0 - 2014-04-07
 * https://github.com/gdibble/backbone-ajaxretry
 * Copyright 2014 Gabriel Dibble; Licensed MIT
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * Extend Backbone.ajax's proxy of $.ajax with Exponential Retries on req fail
 *  * Default settings:
 *      {
 *        "base": 2.67,
 *        "y": 0.25,
 *        "retryCount": 2,
 *      }
 *  * Overwrite default settings via set function
 *  * Pass 'exhaust' option as callback - when retries fail, run this function
 *    (if 'exhaust' method is not passed, retries will end without callback)
 *    Example:
 *      myModel.fetch({
 *        exhaust : function () {
 *          //handle error
 *        }
 *      });
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

var Backbone = require('backbone');
var _        = require('underscore');
//Defaults that can be overridden via set
var settings = {
  base:       2.718281828,
  y:          0.25,
  retryCount: 3
};

//-----------------------------------------------------------------------------

// Helpers:
//  some console.logs have been left (as to be enabled) for those curious minds

//Update current settings, overriding defaults
function setOptions(options) {
  _.defaults(options, settings);
  settings = options;
}

//returns delay in milliseconds
function exponentialDelay(x) {
  return (Math.pow(settings.base, x) - settings.y) * 1000;
}

//hit retry limit
function exhausted() {
  // console.log('exhausted', this.url);
  if (this.hasOwnProperty('exhaust')) {
    // console.log('>>> called this.exhaust', this.exhaust);
    this.exhaust();
  }
}

//recurse the ajax request
function ajaxRetry() {
  var self = this;
  if (this.hasOwnProperty('retries')) {
    this.recursed = this.recursed === undefined ? 0 : this.recursed + 1;
    if (this.recursed < this.retries) {
      setTimeout(function () {
        $.ajax(self);
        // console.log('recursed', self.url, self.recursed, 'in ' + exponentialDelay(self.recursed) + 'ms');
      }, exponentialDelay(this.recursed));
    } else {
      exhausted.call(self);
    }
  } else {
    exhausted.call(self);
  }
}

//-----------------------------------------------------------------------------

//extend for retry functionality
Backbone.ajax = function (options) {
  var args = Array.prototype.slice.call(arguments, 0);
  _.extend(args[0], options ? options : {}, {
    retries: settings.retryCount,
    error:   function () { ajaxRetry.apply(this); }
  });
  return Backbone.$.ajax.apply(Backbone.$, args);
};

module.exports = { set: setOptions };
<commit_msg>closes #3: pass error arguments to exhaust<commit_after>/*!
 * Backbone.js ajaxRetry - v1.0.0 - 2014-04-07
 * https://github.com/gdibble/backbone-ajaxretry
 * Copyright 2014 Gabriel Dibble; Licensed MIT
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * Extend Backbone.ajax's proxy of $.ajax with Exponential Retries on req fail
 *  * Default settings:
 *      {
 *        "base": 2.67,
 *        "y": 0.25,
 *        "retryCount": 2,
 *      }
 *  * Overwrite default settings via set function
 *  * Pass 'exhaust' option as callback - when retries fail, run this function
 *    (if 'exhaust' method is not passed, retries will end without callback)
 *    Example:
 *      myModel.fetch({
 *        exhaust : function () {
 *          //handle error
 *        }
 *      });
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

var Backbone = require('backbone');
var _        = require('underscore');
//Defaults that can be overridden via set
var settings = {
  base:       2.718281828,
  y:          0.25,
  retryCount: 3
};

//-----------------------------------------------------------------------------

// Helpers:
//  some console.logs have been left (as to be enabled) for those curious minds

//Update current settings, overriding defaults
function setOptions(options) {
  _.defaults(options, settings);
  settings = options;
}

//returns delay in milliseconds
function exponentialDelay(x) {
  return (Math.pow(settings.base, x) - settings.y) * 1000;
}

//hit retry limit
function exhausted() {
  // console.log('exhausted', this.url);
  if (this.hasOwnProperty('exhaust')) {
    // console.log('>>> called this.exhaust', this.exhaust);
    this.exhaust.apply(this, arguments);
  }
}

//recurse the ajax request
function ajaxRetry() {
  var self = this;
  if (this.hasOwnProperty('retries')) {
    this.recursed = this.recursed === undefined ? 0 : this.recursed + 1;
    if (this.recursed < this.retries) {
      setTimeout(function () {
        $.ajax(self);
        // console.log('recursed', self.url, self.recursed, 'in ' + exponentialDelay(self.recursed) + 'ms');
      }, exponentialDelay(this.recursed));
    } else {
      exhausted.apply(self, arguments);
    }
  } else {
    exhausted.apply(self, arguments);
  }
}

//-----------------------------------------------------------------------------

//extend for retry functionality
Backbone.ajax = function (options) {
  var args = Array.prototype.slice.call(arguments, 0);
  _.extend(args[0], options ? options : {}, {
    retries: settings.retryCount,
    error:   function () { ajaxRetry.apply(this, arguments); }
  });
  return Backbone.$.ajax.apply(Backbone.$, args);
};

module.exports = { set: setOptions };
<|endoftext|>