<commit_before>import _ from 'lodash';
import bows from 'bows';
import { extent } from 'd3-array';
import { scaleLinear } from 'd3-scale';
import { utcDay } from 'd3-time';
import moment from 'moment-timezone';
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';

import TrendsSVGContainer from './TrendsSVGContainer';

import {
  CGM_READINGS_ONE_DAY,
  MGDL_CLAMP_TOP,
  MMOLL_CLAMP_TOP,
  MGDL_UNITS,
  MMOLL_UNITS,
  trends,
  CGM_DATA_KEY,
  BGM_DATA_KEY,
} from '../../../utils/constants';

import * as datetime from '../../../utils/datetime';
import { weightedCGMCount } from '../../../utils/bloodglucose';

const { extentSizes: { ONE_WEEK, TWO_WEEKS, FOUR_WEEKS } } = trends;

/**
 * getAllDatesInRange
 * @param {String} start - Zulu timestamp (Integer hammertime also OK)
 * @param {String} end - Zulu timestamp (Integer hammertime also OK)
 * @param {Object} timePrefs - object containing timezoneAware Boolean and timezoneName String
 *
 * @return {Array} dates - array of YYYY-MM-DD String dates
 */
export function getAllDatesInRange(start, end, timePrefs) {
  const timezoneName = datetime.getTimezoneFromTimePrefs(timePrefs);
  const dates = [];
  const current = moment.utc(start)
    .tz(timezoneName);
  const excludedBoundary = moment.utc(end);
  while (current.isBefore(excludedBoundary)) {
    dates.push(current.format('YYYY-MM-DD'));
    current.add(1, 'day');
  }
  return dates;
}

/**
 * getLocalizedNoonBeforeUTC
 * @param {String} utc - Zulu timestamp (Integer hammertime also OK)
 * @param {Object} timePrefs - object containing timezoneAware Boolean and timezoneName String
 *
 * @return {JavaScript Date} the closet noon before the input datetime in the given timezone
 */
export function getLocalizedNoonBeforeUTC(utc, timePrefs) {
  if (utc instanceof Date) {
    throw new Error('`utc` must be a ISO-formatted String timestamp or integer hammertime!');
  }
  const timezone = datetime.getTimezoneFromTimePrefs(timePrefs);
  const ceil = datetime.getLocalizedCeiling(utc, timePrefs);
  return moment.utc(ceil.valueOf())
    .tz(timezone)
    .subtract(1, 'day')
    .hours(12)
    .toDate();
}

/**
 * getLocalizedOffset
 * @param {String} utc - Zulu timestamp (Integer hammertime also OK)
 * @param {Object} offset - { amount: integer (+/-), units: 'hour', 'day', &c }
 * @param {Object} timePrefs - object containing timezoneAware Boolean and timezoneName String
 *
 * @return {JavaScript Date} datetime at the specified +/- offset from the input datetime
 *                           inspired by d3-time's offset function: https://github.com/d3/d3-time#interval_offset
 *                           but able to work with an arbitrary timezone
 */
export function getLocalizedOffset(utc, offset, timePrefs) {
  if (utc instanceof Date) {
    throw new Error('`utc` must be a ISO-formatted String timestamp or integer hammertime!');
  }
  const timezone = datetime.getTimezoneFromTimePrefs(timePrefs);
  return moment.utc(utc)
    .tz(timezone)
    .add(offset.amount, offset.units)
    .toDate();
}

export class TrendsContainer extends PureComponent {
  static propTypes = {
    activeDays: PropTypes.shape({
      monday: PropTypes.bool.isRequired,
      tuesday: PropTypes.bool.isRequired,
      wednesday: PropTypes.bool.isRequired,
      thursday: PropTypes.bool.isRequired,
      friday: PropTypes.bool.isRequired,
      saturday: PropTypes.bool.isRequired,
      sunday: PropTypes.bool.isRequired,
    }).isRequired,
    bgPrefs: PropTypes.shape({
      bgBounds: PropTypes.shape({
        veryHighThreshold: PropTypes.number.isRequired,
        targetUpperBound: PropTypes.number.isRequired,
        targetLowerBound: PropTypes.number.isRequired,
        veryLowThreshold: PropTypes.number.isRequired,
      }).isRequired,
      bgUnits: PropTypes.oneOf([MGDL_UNITS, MMOLL_UNITS]).isRequired,
    }).isRequired,
    currentPatientInViewId: PropTypes.string.isRequired,
    extentSize: PropTypes.oneOf([ONE_WEEK, TWO_WEEKS, FOUR_WEEKS]).isRequired,
    initialDatetimeLocation: PropTypes.string,
    loading: PropTypes.bool.isRequired,
    mostRecentDatetimeLocation: PropTypes.string,
    queryDataCount: PropTypes.number.isRequired,
    showingSmbg: PropTypes.bool.isRequired,
    showingCbg: PropTypes.bool.isRequired,
    smbgRangeOverlay: PropTypes.bool.isRequired,
    smbgGrouped: PropTypes.bool.isRequired,
    smbgLines: PropTypes.bool.isRequired,
    timePrefs: PropTypes.shape({
      timezoneAware: PropTypes.bool.isRequired,
      timezoneName: PropTypes.string,
    }).isRequired,
    yScaleClampTop: PropTypes.shape({
      [MGDL_UNITS]: PropTypes.number.isRequired,
      [MMOLL_UNITS]: PropTypes.number.isRequired,
    }).isRequired,
    // data
    data: PropTypes.object.isRequired,
    // handlers
    focusCbgDateTrace: PropTypes.func.isRequired,
    unfocusCbgDateTrace: PropTypes.func.isRequired,
    focusCbgSlice: PropTypes.func.isRequired,
    unfocusCbgSlice: PropTypes.func.isRequired,
    focusSmbg: PropTypes.func.isRequired,
    unfocusSmbg: PropTypes.func.isRequired,
    focusSmbgRange: PropTypes.func.isRequired,
    unfocusSmbgRange: PropTypes.func.isRequired,
    onDatetimeLocationChange: PropTypes.func.isRequired,
    onSelectDate: PropTypes.func.isRequired,
    onSwitchBgDataSource: PropTypes.func.isRequired,
    // viz state
    cbgFlags: PropTypes.shape({
      cbg100Enabled: PropTypes.bool.isRequired,
      cbg80Enabled: PropTypes.bool.isRequired,
      cbg50Enabled: PropTypes.bool.isRequired,
      cbgMedianEnabled: PropTypes.bool.isRequired,
    }).isRequired,
    focusedCbgSlice: PropTypes.shape({
      data: PropTypes.shape({
        firstQuartile: PropTypes.number.isRequired,
        id: PropTypes.string.isRequired,
        max: PropTypes.number.isRequired,
        median: PropTypes.number.isRequired,
        min: PropTypes.number.isRequired,
        msFrom: PropTypes.number.isRequired,
        msTo: PropTypes.number.isRequired,
        msX: PropTypes.number.isRequired,
        ninetiethQuantile: PropTypes.number.isRequired,
        tenthQuantile: PropTypes.number.isRequired,
        thirdQuartile: PropTypes.number.isRequired,
      }),
      position: PropTypes.shape({
        left: PropTypes.number.isRequired,
        tooltipLeft: PropTypes.bool.isRequired,
        topOptions: PropTypes.shape({
          firstQuartile: PropTypes.number.isRequired,
          max: PropTypes.number.isRequired,
          median: PropTypes.number.isRequired,
          min: PropTypes.number.isRequired,
          ninetiethQuantile: PropTypes.number.isRequired,
          tenthQuantile: PropTypes.number.isRequired,
          thirdQuartile: PropTypes.number.isRequired,
        }),
      }),
    }),
    focusedCbgSliceKeys: PropTypes.arrayOf(PropTypes.oneOf([
      'firstQuartile',
      'max',
      'median',
      'min',
      'ninetiethQuantile',
      'tenthQuantile',
      'thirdQuartile',
    ])),
    focusedSmbg: PropTypes.shape({
      allPositions: PropTypes.arrayOf(PropTypes.shape({
        top: PropTypes.number.isRequired,
        left: PropTypes.number.isRequired,
      })),
      allSmbgsOnDate: PropTypes.arrayOf(PropTypes.shape({
        value: PropTypes.number.isRequired,
      })),
      date: PropTypes.string.isRequired,
      datum: PropTypes.shape({
        value: PropTypes.number.isRequired,
      }),
      position: PropTypes.shape({
        top: PropTypes.number.isRequired,
        left: PropTypes.number.isRequired,
      }),
    }),
    focusedSmbgRangeAvg: PropTypes.shape({
      data: PropTypes.shape({
        id: PropTypes.string.isRequired,
        max: PropTypes.number.isRequired,
        mean: PropTypes.number.isRequired,
        min: PropTypes.number.isRequired,
        msX: PropTypes.number.isRequired,
        msFrom: PropTypes.number.isRequired,
        msTo: PropTypes.number.isRequired,
      }).isRequired,
      position: PropTypes.shape({
        left: PropTypes.number.isRequired,
        tooltipLeft: PropTypes.bool.isRequired,
        yPositions: PropTypes.shape({
          max: PropTypes.number.isRequired,
          mean: PropTypes.number.isRequired,
          min: PropTypes.number.isRequired,
        }).isRequired,
      }).isRequired,
    }),
    touched: PropTypes.bool.isRequired,
  };

  static defaultProps = {
    yScaleClampTop: {
      [MGDL_UNITS]: MGDL_CLAMP_TOP,
      [MMOLL_UNITS]: MMOLL_CLAMP_TOP,
    },
  };

  constructor(props) {
    super(props);
    this.log = bows('TrendsContainer');
    this.state = {
      currentCbgData: [],
      currentSmbgData: [],
      dateDomain: null,
      mostRecent: null,
      previousDateDomain: null,
      xScale: null,
      yScale: null,
    };

    this.selectDate = this.selectDate.bind(this);
  }

  UNSAFE_componentWillMount() {
    this.mountData();
  }

  /*
   * NB: we don't do as much here as one might expect
   * because we're using the "expose component functions"
   * strategy of communicating between components
   * (https://facebook.github.io/react/tips/expose-component-functions.html)
   * this is the legacy of blip's interface with the d3.chart-architected
   * smbg version of trends view and thus only remains
   * as a temporary compatibility interface
   */
  UNSAFE_componentWillReceiveProps(nextProps) {
    const loadingJustCompleted = this.props.loading && !nextProps.loading;
    const newDataRecieved = this.props.queryDataCount !== nextProps.queryDataCount;

    if (loadingJustCompleted || newDataRecieved) {
      this.mountData(nextProps);
    }
  }

  mountData(props = this.props) {
    const allBg = _.sortBy(_.cloneDeep(_.get(props, 'data.data.combined', [])), 'normalTime');
    const bgDomain = extent(allBg, d => d.value);
    const currentCbgData = _.filter(allBg, { type: 'cbg' });
    const currentSmbgData = _.filter(allBg, { type: 'smbg' });

    const { bgPrefs: { bgBounds, bgUnits }, yScaleClampTop, mostRecentDatetimeLocation } = props;
    const upperBound = yScaleClampTop[bgUnits];
    const yScaleDomain = [bgDomain[0], upperBound];
    if (!bgDomain[0] || bgDomain[0] > bgBounds.veryLowThreshold) {
      yScaleDomain[0] = bgBounds.veryLowThreshold;
    }
    const yScale = scaleLinear().domain(yScaleDomain).clamp(true);

    // find initial date domain (based on initialDatetimeLocation or current time)
    const { extentSize, initialDatetimeLocation, timePrefs } = props;
    const timezone = datetime.getTimezoneFromTimePrefs(timePrefs);

    const mostRecentCeiling = datetime.getLocalizedCeiling(
      mostRecentDatetimeLocation,
      timePrefs
    ).toISOString();

    const end = initialDatetimeLocation
      ? datetime.getLocalizedCeiling(initialDatetimeLocation, timePrefs).toISOString()
      : mostRecentCeiling;

    const start = moment(end).tz(timezone).subtract(extentSize, 'days').toISOString();
    const dateDomain = [start, end];

    const state = {
      bgDomain: { lo: bgDomain[0], hi: bgDomain[1] },
      currentCbgData,
      currentSmbgData,
      dateDomain: { start: dateDomain[0], end: dateDomain[1] },
      mostRecent: mostRecentCeiling,
      xScale: scaleLinear().domain([0, 864e5]),
      yScale,
    };

    this.setState(state, this.determineDataToShow);
    props.onDatetimeLocationChange(dateDomain);
  }

  getCurrentDay() {
    const { dateDomain: { end } } = this.state;
    return getLocalizedNoonBeforeUTC(end, this.props.timePrefs).toISOString();
  }

  setExtent(newDomain) {
    this.props.onDatetimeLocationChange(newDomain);
  }

  selectDate() {
    const { timePrefs } = this.props;
    return (date) => {
      const noonOnDate = moment.tz(date, datetime.getTimezoneFromTimePrefs(timePrefs))
        .startOf('day')
        .add(12, 'hours')
        .toISOString();
      this.props.onSelectDate(noonOnDate);
    };
  }

  goBack() {
    const { dateDomain: { start: newEnd } } = this.state;
    const start = getLocalizedOffset(newEnd, {
      // negative because we are moving backward in time
      amount: -this.props.extentSize,
      units: 'days',
    }, this.props.timePrefs).toISOString();
    const newDomain = [start, newEnd];
    this.setExtent(newDomain);
  }

  goForward() {
    const { dateDomain: { end: newStart } } = this.state;
    const end = utcDay.offset(new Date(newStart), this.props.extentSize).toISOString();
    const newDomain = [newStart, end];
    this.setExtent(newDomain);
  }

  goToMostRecent() {
    const { mostRecent: end } = this.state;
    const start = utcDay.offset(new Date(end), -this.props.extentSize).toISOString();
    const newDomain = [start, end];
    this.setExtent(newDomain);
  }

  determineDataToShow() {
    const { touched } = this.props;

    if (touched) {
      return;
    }

    const { currentCbgData, currentSmbgData } = this.state;
    const { extentSize, showingCbg } = this.props;
    const minimumCbgs = (extentSize * CGM_READINGS_ONE_DAY) / 2;

    // If we're set to show CBG data, but have less than 50% coverage AND we have SMBG data,
    // switch to SBMG view
    if (showingCbg && weightedCGMCount(currentCbgData) < minimumCbgs && currentSmbgData.length) {
      this.props.onSwitchBgDataSource(null, showingCbg ? BGM_DATA_KEY : CGM_DATA_KEY);
    } else {
      // We don't mark as viewed on the same render cycle as switching the initial bg data source,
      // since both methods update the same `chartPrefs.trends` state tree, and the data source
      // switch would be skipped
      this.props.markTrendsViewed();
    }
  }

  render() {
    const { start: currentStart, end: currentEnd } = this.state.dateDomain;
    const prevStart = _.get(this.state, ['previousDateDomain', 'start']);
    const prevEnd = _.get(this.state, ['previousDateDomain', 'end']);
    let start = currentStart;
    let end = currentEnd;
    if (prevStart && prevEnd) {
      if (currentStart < prevStart) {
        end = prevEnd;
      } else if (prevStart < currentStart) {
        start = prevStart;
      }
    }
    return (
      <TrendsSVGContainer
        activeDays={this.props.activeDays}
        bgPrefs={this.props.bgPrefs}
        smbgData={this.state.currentSmbgData}
        cbgData={this.state.currentCbgData}
        dates={getAllDatesInRange(start, end, this.props.timePrefs)}
        focusCbgDateTrace={this.props.focusCbgDateTrace}
        unfocusCbgDateTrace={this.props.unfocusCbgDateTrace}
        focusCbgSlice={this.props.focusCbgSlice}
        unfocusCbgSlice={this.props.unfocusCbgSlice}
        focusSmbg={this.props.focusSmbg}
        unfocusSmbg={this.props.unfocusSmbg}
        focusSmbgRange={this.props.focusSmbgRange}
        unfocusSmbgRange={this.props.unfocusSmbgRange}
        focusedSlice={this.props.focusedCbgSlice}
        focusedSliceKeys={this.props.focusedCbgSliceKeys}
        focusedSmbgRangeAvgKey={this.props.focusedSmbgRangeAvg}
        focusedSmbg={this.props.focusedSmbg}
        displayFlags={this.props.cbgFlags}
        showingCbg={this.props.showingCbg}
        showingCbgDateTraces={this.props.showingCbgDateTraces}
        showingSmbg={this.props.showingSmbg}
        smbgGrouped={this.props.smbgGrouped}
        smbgLines={this.props.smbgLines}
        smbgRangeOverlay={this.props.smbgRangeOverlay}
        onSelectDate={this.selectDate()}
        xScale={this.state.xScale}
        yScale={this.state.yScale}
      />
    );
  }
}

export default TrendsContainer;
<commit_msg>[RELEASE-1] Merge branch 'develop' into release-1.20.0<commit_after>import _ from 'lodash';
import bows from 'bows';
import { extent } from 'd3-array';
import { scaleLinear } from 'd3-scale';
import moment from 'moment-timezone';
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';

import TrendsSVGContainer from './TrendsSVGContainer';

import {
  CGM_READINGS_ONE_DAY,
  MGDL_CLAMP_TOP,
  MMOLL_CLAMP_TOP,
  MGDL_UNITS,
  MMOLL_UNITS,
  trends,
  CGM_DATA_KEY,
  BGM_DATA_KEY,
} from '../../../utils/constants';

import * as datetime from '../../../utils/datetime';
import { weightedCGMCount } from '../../../utils/bloodglucose';

const { extentSizes: { ONE_WEEK, TWO_WEEKS, FOUR_WEEKS } } = trends;

/**
 * getAllDatesInRange
 * @param {String} start - Zulu timestamp (Integer hammertime also OK)
 * @param {String} end - Zulu timestamp (Integer hammertime also OK)
 * @param {Object} timePrefs - object containing timezoneAware Boolean and timezoneName String
 *
 * @return {Array} dates - array of YYYY-MM-DD String dates
 */
export function getAllDatesInRange(start, end, timePrefs) {
  const timezoneName = datetime.getTimezoneFromTimePrefs(timePrefs);
  const dates = [];
  const current = moment.utc(start)
    .tz(timezoneName);
  const excludedBoundary = moment.utc(end);
  while (current.isBefore(excludedBoundary)) {
    dates.push(current.format('YYYY-MM-DD'));
    current.add(1, 'day');
  }
  return dates;
}

/**
 * getLocalizedNoonBeforeUTC
 * @param {String} utc - Zulu timestamp (Integer hammertime also OK)
 * @param {Object} timePrefs - object containing timezoneAware Boolean and timezoneName String
 *
 * @return {JavaScript Date} the closet noon before the input datetime in the given timezone
 */
export function getLocalizedNoonBeforeUTC(utc, timePrefs) {
  if (utc instanceof Date) {
    throw new Error('`utc` must be a ISO-formatted String timestamp or integer hammertime!');
  }
  const timezone = datetime.getTimezoneFromTimePrefs(timePrefs);
  const ceil = datetime.getLocalizedCeiling(utc, timePrefs);
  return moment.utc(ceil.valueOf())
    .tz(timezone)
    .subtract(1, 'day')
    .hours(12)
    .toDate();
}

/**
 * getLocalizedOffset
 * @param {String} utc - Zulu timestamp (Integer hammertime also OK)
 * @param {Object} offset - { amount: integer (+/-), units: 'hour', 'day', &c }
 * @param {Object} timePrefs - object containing timezoneAware Boolean and timezoneName String
 *
 * @return {JavaScript Date} datetime at the specified +/- offset from the input datetime
 *                           inspired by d3-time's offset function: https://github.com/d3/d3-time#interval_offset
 *                           but able to work with an arbitrary timezone
 */
export function getLocalizedOffset(utc, offset, timePrefs) {
  if (utc instanceof Date) {
    throw new Error('`utc` must be a ISO-formatted String timestamp or integer hammertime!');
  }
  const timezone = datetime.getTimezoneFromTimePrefs(timePrefs);
  return moment.utc(utc)
    .tz(timezone)
    .add(offset.amount, offset.units)
    .toDate();
}

export class TrendsContainer extends PureComponent {
  static propTypes = {
    activeDays: PropTypes.shape({
      monday: PropTypes.bool.isRequired,
      tuesday: PropTypes.bool.isRequired,
      wednesday: PropTypes.bool.isRequired,
      thursday: PropTypes.bool.isRequired,
      friday: PropTypes.bool.isRequired,
      saturday: PropTypes.bool.isRequired,
      sunday: PropTypes.bool.isRequired,
    }).isRequired,
    bgPrefs: PropTypes.shape({
      bgBounds: PropTypes.shape({
        veryHighThreshold: PropTypes.number.isRequired,
        targetUpperBound: PropTypes.number.isRequired,
        targetLowerBound: PropTypes.number.isRequired,
        veryLowThreshold: PropTypes.number.isRequired,
      }).isRequired,
      bgUnits: PropTypes.oneOf([MGDL_UNITS, MMOLL_UNITS]).isRequired,
    }).isRequired,
    currentPatientInViewId: PropTypes.string.isRequired,
    extentSize: PropTypes.oneOf([ONE_WEEK, TWO_WEEKS, FOUR_WEEKS]).isRequired,
    initialDatetimeLocation: PropTypes.string,
    loading: PropTypes.bool.isRequired,
    mostRecentDatetimeLocation: PropTypes.string,
    queryDataCount: PropTypes.number.isRequired,
    showingSmbg: PropTypes.bool.isRequired,
    showingCbg: PropTypes.bool.isRequired,
    smbgRangeOverlay: PropTypes.bool.isRequired,
    smbgGrouped: PropTypes.bool.isRequired,
    smbgLines: PropTypes.bool.isRequired,
    timePrefs: PropTypes.shape({
      timezoneAware: PropTypes.bool.isRequired,
      timezoneName: PropTypes.string,
    }).isRequired,
    yScaleClampTop: PropTypes.shape({
      [MGDL_UNITS]: PropTypes.number.isRequired,
      [MMOLL_UNITS]: PropTypes.number.isRequired,
    }).isRequired,
    // data
    data: PropTypes.object.isRequired,
    // handlers
    focusCbgDateTrace: PropTypes.func.isRequired,
    unfocusCbgDateTrace: PropTypes.func.isRequired,
    focusCbgSlice: PropTypes.func.isRequired,
    unfocusCbgSlice: PropTypes.func.isRequired,
    focusSmbg: PropTypes.func.isRequired,
    unfocusSmbg: PropTypes.func.isRequired,
    focusSmbgRange: PropTypes.func.isRequired,
    unfocusSmbgRange: PropTypes.func.isRequired,
    onDatetimeLocationChange: PropTypes.func.isRequired,
    onSelectDate: PropTypes.func.isRequired,
    onSwitchBgDataSource: PropTypes.func.isRequired,
    // viz state
    cbgFlags: PropTypes.shape({
      cbg100Enabled: PropTypes.bool.isRequired,
      cbg80Enabled: PropTypes.bool.isRequired,
      cbg50Enabled: PropTypes.bool.isRequired,
      cbgMedianEnabled: PropTypes.bool.isRequired,
    }).isRequired,
    focusedCbgSlice: PropTypes.shape({
      data: PropTypes.shape({
        firstQuartile: PropTypes.number.isRequired,
        id: PropTypes.string.isRequired,
        max: PropTypes.number.isRequired,
        median: PropTypes.number.isRequired,
        min: PropTypes.number.isRequired,
        msFrom: PropTypes.number.isRequired,
        msTo: PropTypes.number.isRequired,
        msX: PropTypes.number.isRequired,
        ninetiethQuantile: PropTypes.number.isRequired,
        tenthQuantile: PropTypes.number.isRequired,
        thirdQuartile: PropTypes.number.isRequired,
      }),
      position: PropTypes.shape({
        left: PropTypes.number.isRequired,
        tooltipLeft: PropTypes.bool.isRequired,
        topOptions: PropTypes.shape({
          firstQuartile: PropTypes.number.isRequired,
          max: PropTypes.number.isRequired,
          median: PropTypes.number.isRequired,
          min: PropTypes.number.isRequired,
          ninetiethQuantile: PropTypes.number.isRequired,
          tenthQuantile: PropTypes.number.isRequired,
          thirdQuartile: PropTypes.number.isRequired,
        }),
      }),
    }),
    focusedCbgSliceKeys: PropTypes.arrayOf(PropTypes.oneOf([
      'firstQuartile',
      'max',
      'median',
      'min',
      'ninetiethQuantile',
      'tenthQuantile',
      'thirdQuartile',
    ])),
    focusedSmbg: PropTypes.shape({
      allPositions: PropTypes.arrayOf(PropTypes.shape({
        top: PropTypes.number.isRequired,
        left: PropTypes.number.isRequired,
      })),
      allSmbgsOnDate: PropTypes.arrayOf(PropTypes.shape({
        value: PropTypes.number.isRequired,
      })),
      date: PropTypes.string.isRequired,
      datum: PropTypes.shape({
        value: PropTypes.number.isRequired,
      }),
      position: PropTypes.shape({
        top: PropTypes.number.isRequired,
        left: PropTypes.number.isRequired,
      }),
    }),
    focusedSmbgRangeAvg: PropTypes.shape({
      data: PropTypes.shape({
        id: PropTypes.string.isRequired,
        max: PropTypes.number.isRequired,
        mean: PropTypes.number.isRequired,
        min: PropTypes.number.isRequired,
        msX: PropTypes.number.isRequired,
        msFrom: PropTypes.number.isRequired,
        msTo: PropTypes.number.isRequired,
      }).isRequired,
      position: PropTypes.shape({
        left: PropTypes.number.isRequired,
        tooltipLeft: PropTypes.bool.isRequired,
        yPositions: PropTypes.shape({
          max: PropTypes.number.isRequired,
          mean: PropTypes.number.isRequired,
          min: PropTypes.number.isRequired,
        }).isRequired,
      }).isRequired,
    }),
    touched: PropTypes.bool.isRequired,
  };

  static defaultProps = {
    yScaleClampTop: {
      [MGDL_UNITS]: MGDL_CLAMP_TOP,
      [MMOLL_UNITS]: MMOLL_CLAMP_TOP,
    },
  };

  constructor(props) {
    super(props);
    this.log = bows('TrendsContainer');
    this.state = {
      currentCbgData: [],
      currentSmbgData: [],
      dateDomain: null,
      mostRecent: null,
      previousDateDomain: null,
      xScale: null,
      yScale: null,
    };

    this.selectDate = this.selectDate.bind(this);
  }

  UNSAFE_componentWillMount() {
    this.mountData();
  }

  /*
   * NB: we don't do as much here as one might expect
   * because we're using the "expose component functions"
   * strategy of communicating between components
   * (https://facebook.github.io/react/tips/expose-component-functions.html)
   * this is the legacy of blip's interface with the d3.chart-architected
   * smbg version of trends view and thus only remains
   * as a temporary compatibility interface
   */
  UNSAFE_componentWillReceiveProps(nextProps) {
    const loadingJustCompleted = this.props.loading && !nextProps.loading;
    const newDataRecieved = this.props.queryDataCount !== nextProps.queryDataCount;

    if (loadingJustCompleted || newDataRecieved) {
      this.mountData(nextProps);
    }
  }

  mountData(props = this.props) {
    const allBg = _.sortBy(_.cloneDeep(_.get(props, 'data.data.combined', [])), 'normalTime');
    const bgDomain = extent(allBg, d => d.value);
    const currentCbgData = _.filter(allBg, { type: 'cbg' });
    const currentSmbgData = _.filter(allBg, { type: 'smbg' });

    const { bgPrefs: { bgBounds, bgUnits }, yScaleClampTop, mostRecentDatetimeLocation } = props;
    const upperBound = yScaleClampTop[bgUnits];
    const yScaleDomain = [bgDomain[0], upperBound];
    if (!bgDomain[0] || bgDomain[0] > bgBounds.veryLowThreshold) {
      yScaleDomain[0] = bgBounds.veryLowThreshold;
    }
    const yScale = scaleLinear().domain(yScaleDomain).clamp(true);

    // find initial date domain (based on initialDatetimeLocation or current time)
    const { extentSize, initialDatetimeLocation, timePrefs } = props;
    const timezone = datetime.getTimezoneFromTimePrefs(timePrefs);

    const mostRecentCeiling = datetime.getLocalizedCeiling(
      mostRecentDatetimeLocation,
      timePrefs
    ).toISOString();

    const end = initialDatetimeLocation
      ? datetime.getLocalizedCeiling(initialDatetimeLocation, timePrefs).toISOString()
      : mostRecentCeiling;

    const start = moment(end).tz(timezone).subtract(extentSize, 'days').toISOString();
    const dateDomain = [start, end];

    const state = {
      bgDomain: { lo: bgDomain[0], hi: bgDomain[1] },
      currentCbgData,
      currentSmbgData,
      dateDomain: { start: dateDomain[0], end: dateDomain[1] },
      mostRecent: mostRecentCeiling,
      xScale: scaleLinear().domain([0, 864e5]),
      yScale,
    };

    this.setState(state, this.determineDataToShow);
    props.onDatetimeLocationChange(dateDomain);
  }

  getCurrentDay() {
    const { dateDomain: { end } } = this.state;
    return getLocalizedNoonBeforeUTC(end, this.props.timePrefs).toISOString();
  }

  setExtent(newDomain) {
    this.props.onDatetimeLocationChange(newDomain);
  }

  selectDate() {
    const { timePrefs } = this.props;
    return (date) => {
      const noonOnDate = moment.tz(date, datetime.getTimezoneFromTimePrefs(timePrefs))
        .startOf('day')
        .add(12, 'hours')
        .toISOString();
      this.props.onSelectDate(noonOnDate);
    };
  }

  goBack() {
    const { dateDomain: { start: newEnd } } = this.state;
    const start = getLocalizedOffset(newEnd, {
      // negative because we are moving backward in time
      amount: -this.props.extentSize,
      units: 'days',
    }, this.props.timePrefs).toISOString();
    const newDomain = [start, newEnd];
    this.setExtent(newDomain);
  }

  goForward() {
    const { dateDomain: { end: newStart } } = this.state;
    const end = getLocalizedOffset(newStart, {
      amount: this.props.extentSize,
      units: 'days',
    }, this.props.timePrefs).toISOString();
    const newDomain = [newStart, end];
    this.setExtent(newDomain);
  }

  goToMostRecent() {
    const { mostRecent: end } = this.state;
    const start = getLocalizedOffset(end, {
      amount: -this.props.extentSize,
      units: 'days',
    }, this.props.timePrefs).toISOString();
    const newDomain = [start, end];
    this.setExtent(newDomain);
  }

  determineDataToShow() {
    const { touched } = this.props;

    if (touched) {
      return;
    }

    const { currentCbgData, currentSmbgData } = this.state;
    const { extentSize, showingCbg } = this.props;
    const minimumCbgs = (extentSize * CGM_READINGS_ONE_DAY) / 2;

    // If we're set to show CBG data, but have less than 50% coverage AND we have SMBG data,
    // switch to SBMG view
    if (showingCbg && weightedCGMCount(currentCbgData) < minimumCbgs && currentSmbgData.length) {
      this.props.onSwitchBgDataSource(null, showingCbg ? BGM_DATA_KEY : CGM_DATA_KEY);
    } else {
      // We don't mark as viewed on the same render cycle as switching the initial bg data source,
      // since both methods update the same `chartPrefs.trends` state tree, and the data source
      // switch would be skipped
      this.props.markTrendsViewed();
    }
  }

  render() {
    const { start: currentStart, end: currentEnd } = this.state.dateDomain;
    const prevStart = _.get(this.state, ['previousDateDomain', 'start']);
    const prevEnd = _.get(this.state, ['previousDateDomain', 'end']);
    let start = currentStart;
    let end = currentEnd;
    if (prevStart && prevEnd) {
      if (currentStart < prevStart) {
        end = prevEnd;
      } else if (prevStart < currentStart) {
        start = prevStart;
      }
    }
    return (
      <TrendsSVGContainer
        activeDays={this.props.activeDays}
        bgPrefs={this.props.bgPrefs}
        smbgData={this.state.currentSmbgData}
        cbgData={this.state.currentCbgData}
        dates={getAllDatesInRange(start, end, this.props.timePrefs)}
        focusCbgDateTrace={this.props.focusCbgDateTrace}
        unfocusCbgDateTrace={this.props.unfocusCbgDateTrace}
        focusCbgSlice={this.props.focusCbgSlice}
        unfocusCbgSlice={this.props.unfocusCbgSlice}
        focusSmbg={this.props.focusSmbg}
        unfocusSmbg={this.props.unfocusSmbg}
        focusSmbgRange={this.props.focusSmbgRange}
        unfocusSmbgRange={this.props.unfocusSmbgRange}
        focusedSlice={this.props.focusedCbgSlice}
        focusedSliceKeys={this.props.focusedCbgSliceKeys}
        focusedSmbgRangeAvgKey={this.props.focusedSmbgRangeAvg}
        focusedSmbg={this.props.focusedSmbg}
        displayFlags={this.props.cbgFlags}
        showingCbg={this.props.showingCbg}
        showingCbgDateTraces={this.props.showingCbgDateTraces}
        showingSmbg={this.props.showingSmbg}
        smbgGrouped={this.props.smbgGrouped}
        smbgLines={this.props.smbgLines}
        smbgRangeOverlay={this.props.smbgRangeOverlay}
        onSelectDate={this.selectDate()}
        xScale={this.state.xScale}
        yScale={this.state.yScale}
      />
    );
  }
}

export default TrendsContainer;
<|endoftext|>


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

  function Options ( schema, initial ) {
    if ( typeof schema !== 'object' ) { throw new TypeError('First argument must be an object with scheme of default options.'); }
    this._schema    = schema;
    this._options   = {};
    this._callbacks = {};
    this.set( initial, true );
    return this;
  }

  Options.extend = function() {
    var target = arguments[0],
    args = Array.prototype.slice.call(arguments), source;
    args.shift();
    for (var i = 0; i < args.length; i++) {
      source = args[i];
      for ( var key in source ) { target[key] = source[key]; }
    }
    return target;
  };

  Options.checkType = function(val, schema) {
    var type = typeof val, isNullable = val === null && schema.nullable;
    return ( schema.type instanceof Array ) ? schema.type.indexOf( type ) >= 0 || isNullable : type === schema.type || isNullable;
  };

  Options.prototype.set = function( obj, isNew ) {
    var schema = this._schema,
    newOptions = isNew ? {} : this.get(),
    defaults = {},
    option, callback;
    obj = obj || {};

    // Check options
    for ( option in obj ) {
      var val = obj[ option ],
      defOption = schema[ option ];

      if ( defOption !== undefined ) {
        // unchangeable
        if ( defOption.unchangeable && !isNew ) {
          throw new Error( 'Option \"' + option + '\" could be setted once at the begining.' );
        }
        // wrong type
        if ( !Options.checkType(val, defOption) ) {
          var msg = 'Option \"' + option + '\" must be ' +
            ( defOption.type instanceof Array ? defOption.type.join(', ') : defOption.type ) +
            ( defOption.nullable ? ' or null.' : '.' );
          throw new TypeError( msg );
        }
        // out of values
        if ( defOption.values && defOption.values.indexOf(val) < 0 ) {
          throw new RangeError( 'Option \"' + option + '\" only could be in these values: \"' + defOption.values.join('\", \"') + '\".' );
        }
      }
    }
    // Create new options object
    if ( isNew ) {
      for ( option in schema ) {
        if ( schema[ option ]['default'] !== undefined ) { defaults[ option ] = schema[ option ]['default']; }
      }
    }
    newOptions = isNew ? Options.extend( defaults, obj ) : obj;
    // Callbacks
    for ( option in obj ) {
      if ( (callback = this._callbacks[option]) ) {
        obj[option] = callback.call( this, obj[option] );
      }
    }
    this._options = Options.extend( this._options, newOptions );
  };

  Options.prototype.get = function( opt ) {
    return opt ? this._options[ opt ] : Options.extend( {}, this._options );
  };

  Options.prototype.on = function( option, cb ) {
    this._callbacks[ option ] = cb;
  };

  Options.prototype.off = function( option ) {
    if ( this._callbacks[ option ] ) { delete this._callbacks[option]; }
  };

  window.SuperOptions = Options;

})(window);<commit_msg>Added function for checking if value in an array.<commit_after>(function(window) {
  'use strict';

  function Options ( schema, initial ) {
    if ( typeof schema !== 'object' ) { throw new TypeError('First argument must be an object with scheme of default options.'); }
    this._schema    = schema;
    this._options   = {};
    this._callbacks = {};
    this.set( initial, true );
    return this;
  }

  var itContains = function( array, elem ) {
    if ( array instanceof Array && array.length > 0 && elem !== undefined ) {
      for (var i = 0; i < array.length; i++) { if (elem === array[i]) {return true;} }
    }
    return false;
  };

  Options.extend = function() {
    var target = arguments[0],
    args = Array.prototype.slice.call(arguments), source;
    args.shift();
    for (var i = 0; i < args.length; i++) {
      source = args[i];
      for ( var key in source ) { target[key] = source[key]; }
    }
    return target;
  };

  Options.checkType = function(val, schema) {
    var type = typeof val, isNullable = val === null && schema.nullable;
    return ( schema.type instanceof Array ) ? itContains(schema.type, type) || isNullable : type === schema.type || isNullable;
  };

  Options.prototype.set = function( obj, isNew ) {
    var schema = this._schema,
    newOptions = isNew ? {} : this.get(),
    defaults = {},
    option, callback;
    obj = obj || {};

    // Check options
    for ( option in obj ) {
      var val = obj[ option ],
      defOption = schema[ option ];

      if ( defOption !== undefined ) {
        // unchangeable
        if ( defOption.unchangeable && !isNew ) {
          throw new Error( 'Option \"' + option + '\" could be setted once at the begining.' );
        }
        // wrong type
        if ( !Options.checkType(val, defOption) ) {
          var msg = 'Option \"' + option + '\" must be ' +
            ( defOption.type instanceof Array ? defOption.type.join(', ') : defOption.type ) +
            ( defOption.nullable ? ' or null.' : '.' );
          throw new TypeError( msg );
        }
        // out of values
        if ( defOption.values && !itContains(defOption.values, val) ) {
          throw new RangeError( 'Option \"' + option + '\" only could be in these values: \"' + defOption.values.join('\", \"') + '\".' );
        }
      }
    }
    // Create new options object
    if ( isNew ) {
      for ( option in schema ) {
        if ( schema[ option ]['default'] !== undefined ) { defaults[ option ] = schema[ option ]['default']; }
      }
    }
    newOptions = isNew ? Options.extend( defaults, obj ) : obj;
    // Callbacks
    for ( option in obj ) {
      if ( (callback = this._callbacks[option]) ) {
        obj[option] = callback.call( this, obj[option] );
      }
    }
    this._options = Options.extend( this._options, newOptions );
  };

  Options.prototype.get = function( opt ) {
    return opt ? this._options[ opt ] : Options.extend( {}, this._options );
  };

  Options.prototype.on = function( option, cb ) {
    this._callbacks[ option ] = cb;
  };

  Options.prototype.off = function( option ) {
    if ( this._callbacks[ option ] ) { delete this._callbacks[option]; }
  };

  window.SuperOptions = Options;

})(window);<|endoftext|>


<commit_before>lively.Config.add('modulesOnWorldLoad', "lively.ide.codeeditor.BetterConfig");
<commit_msg>cloxp config setup for lively web deployment<commit_after>lively.Config.add('modulesOnWorldLoad', "lively.ide.codeeditor.BetterConfig");

// set uer based on cloxp assignment, also take care of the l2l connection
// which won't work over the proxy
var cookie = document.cookie.split(";").detect(function(ea) { return ea.include("cloxp-assignment"); });
var port = cookie && Number(cookie.split("=").last());
if (port) {
  lively.whenLoaded(function(w) { $world.setCurrentUser("cloxp-user-" + port); })
  lively.Config.addOption("nodeJSWebSocketURL", 'http://lively-web.org:' + port + '/nodejs');
}
<|endoftext|>


<commit_before>'use strict'

const apiUrl = document.querySelector('#api-url')
const apiKey = document.querySelector('#api-key')
const saveBtn = document.querySelector('#save')

const enableSave = () => {
  saveBtn.value = 'Save'
  saveBtn.disabled = false
}

const saveOptions = (e) => {
  e.preventDefault()
  browser.storage.local.set({
    'apiUrl': apiUrl.value,
    'apiKey': apiKey.value
  })
  saveBtn.value = 'Saved'
  saveBtn.disabled = true
}

const restoreOptions = () => {
  const setApiUrl = (result) => {
    apiUrl.value = result['apiUrl'] || 'https://api.runmycode.online/run'
  }

  const setApiKey = (result) => {
    apiKey.value = result['apiKey'] || ''
  }

  const onError = (error) => {
    console.log(`Error: ${error}`)
  }

  browser.storage.local.get('apiUrl').then(setApiUrl, onError)
  browser.storage.local.get('apiKey').then(setApiKey, onError)
}

document.addEventListener('DOMContentLoaded', restoreOptions)
saveBtn.onclick = saveOptions
apiUrl.oninput = enableSave
apiKey.oninput = enableSave
<commit_msg>fix: save extension options after restoring<commit_after>'use strict'

const apiUrl = document.querySelector('#api-url')
const apiKey = document.querySelector('#api-key')
const saveBtn = document.querySelector('#save')

const enableSave = () => {
  saveBtn.value = 'Save'
  saveBtn.disabled = false
}

const saveOptions = (e) => {
  if (e) e.preventDefault()
  browser.storage.local.set({
    'apiUrl': apiUrl.value,
    'apiKey': apiKey.value
  })
  saveBtn.value = 'Saved'
  saveBtn.disabled = true
}

const restoreOptions = () => {
  const getApiUrl = browser.storage.local.get('apiUrl')
  const getApiKey = browser.storage.local.get('apiKey')
  Promise.all([getApiUrl, getApiKey])
  .then((result) => {
    apiUrl.value = result[0]['apiUrl'] || 'https://api.runmycode.online/run'
    apiKey.value = result[1]['apiKey'] || ''
    saveOptions()
  })
  .catch((error) => {
    console.log('Error: ', error)
  })
}

document.addEventListener('DOMContentLoaded', restoreOptions)
saveBtn.addEventListener('click', saveOptions)
apiUrl.addEventListener('input', enableSave)
apiKey.addEventListener('input', enableSave)
<|endoftext|>


<commit_before>/*  MoST Subtitle Player :: Implementation Overview

Code is inserted into the target web page, making syncing possible.

Subtitles are downloaded via DownSub (but we could have used the api).

A transparent overlay is created as a div using Zepto; see addOverlay.

Subtitles are configurable with CSS in a Chrome options page.

*/

// Tip: Use the 'debugger' command to insert a breakpoint!

/*  Developer's notes

In order to access the global variable 'player' at Viki when debugging using
Chrome's Developer Tools, the 'Elements' tab must be active.

UPDATE 2016-10-07: player.player.tech_.textTracks() will be a list of all
subtitles for the current episode! This means that DownSub could be replaced.

*/

// One subtitle writer for each language.
var writers = [];

$(document).ready(function () {

    function init(message, sender, callback) {
        // Stop the old "instance" if there is one.
        clean();

        // Initiate communication.
        startSync();
        addOverlay(message.style);
        writers = [];
        for (var i = 0; i < message.languages.length; i++) {
            var srt = message.subtitles[message.languages[i]];
            writers[i] = new Writer(message.languages[i], srt);
            writers[i].play();
        }
    }

    chrome.extension.onMessage.addListener(init);

});

function clean() {
    for (var i = 0; i < writers.length; i++) {
        writers[i].stop();
    }

    $("#most-overlay").remove();
}

function Writer(language, srt) {
    // The decoded srt file is organized into pages.
    this.pages = parseSubtitles(srt);
    this.current;

    // Text is continuously written as html.
    this.element = document.createElement("div");
    this.element.className = "most-subtitles most-" + language;
    $("#most-overlay")[0].appendChild(this.element);

    this.timer;

    this.play = function () {
        this.timer = true;

        var that = this;

        // To be run many times per second.
        function updateRepeatedly() {
            var period = 50;  // sleep time between updates

            // Get the local playback time (in milliseconds, starting at 0).
            var syncEl = document.getElementById("most-sync");
            var local = new Date().getTime() - parseInt(syncEl.innerHTML);

            // Write current subtitle page to screen.
            for (var i = 0; i < that.pages.length; i++) {  // TODO binary search?
                var page = that.pages[i];
                // the " + period" here is just for fine tuning, not optimal
                if (page['loc0'] <= local + period
                        && local + period <= page['loc1']
                        && that.current != language + "-" + i) {
                    that.show(page['s'], page['loc1']-local, language + "-" + i);
                }
            }

            // Reschedule, creating an infinite loop.
            if (that.timer) {
                that.timer = window.setTimeout(updateRepeatedly, period);
            }
        };

        updateRepeatedly();
    }

    this.stop = function() {
        this.timer = false;
    }

    // Show text, typically a subtitle page.
    this.show = function(text, duration, uid) {
        // Remove old text. (This is an exceptional case.)
        if (this.current !== undefined) {
            this.hide(this.current);
        }

        // Update video geometry. TODO: Should this line be somewhere else?
        updateBasicCSS();

        // Write the text to screen.
        var textEl = document.createElement("div");
        textEl.className = "uid-" + uid;
        textEl.innerHTML = text;
        this.element.appendChild(textEl);

        // Don't rewrite it a lot.
        this.current = uid;

        // Remember to remove it after a while.
        var that = this;
        window.setTimeout(function () {  // TODO: don't remove it if paused.
            if (that.current === uid) {
                that.hide(uid);
                that.current = undefined;
            }
        }, duration);
    }

    // Hide text. Inverse of 'show'.
    this.hide = function(uid) {
        $(".uid-" + uid).remove();
    }

    // Info text after trying to initialize the writer.
    if (this.pages.length > 0) {
        this.show("Loaded " + this.pages.length + " pages.", 2000,
            language + "-info");
    } else {
        this.show("Failed to load subtitles for language " + language, 4000,
            language + "-info");
    }

}

// Ugly hack independent of this scope (only its string will be injected).
function specialCode () {
    // Will hold the unix time minus local playback time (in milliseconds).
    $("body").append("

");

    // Determine how the target player will tell its current position
    // (in seconds).
    var getCurrentTime = function() {
        // Look for the regular player, subtitle editor (flash or html5)
        // and segment editor.
        if ("player" in window) {
            return player.getCurrentTime();
        }
        if ("subber_player_flash_api" in window) {
            return subber_player_flash_api.player.currentTime();
        }
        if ("subber_player_html5_api" in window) {
            return subber_player_html5_api.player.currentTime();
        }
    }

    // Export this position continuously in html format (using the DOM).
    function syncForever() {
        var local = Math.round(getCurrentTime() * 1000);
        var syncVal = new Date().getTime() - local;
        document.getElementById("most-sync").innerHTML = syncVal;
        window.setTimeout(syncForever, 100);
    };

    syncForever();
}

function startSync() {
    // Inject the ugly hack into the web page.
    var script = document.createElement('script');
    script.appendChild(document.createTextNode(
        '('+ specialCode +')();'));
    (document.body || document.head || document.documentElement)
        .appendChild(script);
}

function updateBasicCSS() {
    // Find the positioning of the video player, or use (bad) defaults.
    var video = $("#video-player_html5_api")[0] || $("#subber_player")[0];
    var videoRect = video.getBoundingClientRect();

    // Configure the looks and position.
    //$("#most-overlay").removeAttr("style"); ?
    $("#most-overlay").css({
        position: "fixed",
        width: videoRect.width + "px",
        height: videoRect.height + "px",
        left: videoRect.left,
        top: videoRect.top,
        zIndex: "2147483647",
        pointerEvents: "none",
        textAlign: "center"
    });
}

function addOverlay(style) {
    // Create the overlay
    overlay = $("

");
    $("body").append(overlay);

    // Set the subtitle style
    updateBasicCSS();
    var styleEl = document.createElement("style");
    styleEl.innerHTML = style;
    $("head").append(styleEl);
}

// Convert an srt time string to integer.
function strToMs(loc) {
    var h, m, s, ms;
    [h, m, s, ms] = loc.replace(',', ':').split(':');
    [h, m, s, ms] = [parseInt(h), parseInt(m), parseInt(s), parseInt(ms)];
    return ((h*60 + m)*60 + s)*1000 + ms;
}

// Turn an srt into computation-friendly form: pages.
function parseSubtitles(srt) {
    if (srt === undefined || srt === "") return [];

    var pages = [];

    var srt2 = srt.replace(/\r\n|\r|\n/g, '\n')  // Normalize newlines.
                  .trim(srt)
                  .split('\n\n');

    // Parse each page.
    for (var i = 0; i < srt2.length; i++) {
        var a, b, c, d, e, loc0, loc1;
        a = srt2[i];
        [b, c] = [a.slice(0, a.indexOf("\n")), a.slice(a.indexOf("\n")+1)];
        [d, e] = [c.slice(0, c.indexOf("\n")), c.slice(c.indexOf("\n")+1)];
        [loc0, loc1] = d.split(" --> ");

        // The following line improves security but comes at the cost of
        // not supporting arbitrary html markup.
        e = e.replace(/<\/?[^>]+(>|$)/g, function(part) {
            var accept = ['', '', '
', '
', '', ''];
            for (var i = 0; i < accept.length; i++) {
                if (part === accept[i]) return part;
            }
            return "";
        });

        pages.push({'loc0': strToMs(loc0), 'loc1': strToMs(loc1), 's': e});
    }

    return pages;
}

<commit_msg>variable name update<commit_after>/*  MoST Subtitle Player :: Implementation Overview

Code is inserted into the target web page, making syncing possible.

Subtitles are downloaded via DownSub (but we could have used the api).

A transparent overlay is created as a div using Zepto; see addOverlay.

Subtitles are configurable with CSS in a Chrome options page.

*/

// Tip: Use the 'debugger' command to insert a breakpoint!

/*  Developer's notes

In order to access the global variable 'player' at Viki when debugging using
Chrome's Developer Tools, the 'Elements' tab must be active.

UPDATE 2016-10-07: player.player.tech_.textTracks() will be a list of all
subtitles for the current episode! This means that DownSub could be replaced.

*/

// One subtitle writer for each language.
var writers = [];

$(document).ready(function () {

    function init(message, sender, callback) {
        // Stop the old "instance" if there is one.
        clean();

        // Initiate communication.
        startSync();
        addOverlay(message.style);
        writers = [];
        for (var i = 0; i < message.languages.length; i++) {
            var srt = message.subtitles[message.languages[i]];
            writers[i] = new Writer(message.languages[i], srt);
            writers[i].play();
        }
    }

    chrome.extension.onMessage.addListener(init);

});

function clean() {
    for (var i = 0; i < writers.length; i++) {
        writers[i].stop();
    }

    $("#most-overlay").remove();
}

function Writer(language, srt) {
    // The decoded srt file is organized into pages.
    this.pages = parseSubtitles(srt);
    this.current;

    // Text is continuously written as html.
    this.element = document.createElement("div");
    this.element.className = "most-subtitles most-" + language;
    $("#most-overlay")[0].appendChild(this.element);

    this.timer;

    this.play = function () {
        this.timer = true;

        var that = this;

        // To be run many times per second.
        function updateRepeatedly() {
            var period = 50;  // sleep time between updates

            // Get the local playback time (in milliseconds, starting at 0).
            var syncEl = document.getElementById("most-sync");
            var local = new Date().getTime() - parseInt(syncEl.innerHTML);

            // Write current subtitle page to screen.
            for (var i = 0; i < that.pages.length; i++) {  // TODO binary search?
                var page = that.pages[i];
                // the " + period" here is just for fine tuning, not optimal
                if (page['loc0'] <= local + period
                        && local + period <= page['loc1']
                        && that.current != language + "-" + i) {
                    that.show(page['s'], page['loc1']-local, language + "-" + i);
                }
            }

            // Reschedule, creating an infinite loop.
            if (that.timer) {
                that.timer = window.setTimeout(updateRepeatedly, period);
            }
        };

        updateRepeatedly();
    }

    this.stop = function() {
        this.timer = false;
    }

    // Show text, typically a subtitle page.
    this.show = function(text, duration, uid) {
        // Remove old text. (This is an exceptional case.)
        if (this.current !== undefined) {
            this.hide(this.current);
        }

        // Update video geometry. TODO: Should this line be somewhere else?
        updateBasicCSS();

        // Write the text to screen.
        var textEl = document.createElement("div");
        textEl.className = "uid-" + uid;
        textEl.innerHTML = text;
        this.element.appendChild(textEl);

        // Don't rewrite it a lot.
        this.current = uid;

        // Remember to remove it after a while.
        var that = this;
        window.setTimeout(function () {  // TODO: don't remove it if paused.
            if (that.current === uid) {
                that.hide(uid);
                that.current = undefined;
            }
        }, duration);
    }

    // Hide text. Inverse of 'show'.
    this.hide = function(uid) {
        $(".uid-" + uid).remove();
    }

    // Info text after trying to initialize the writer.
    if (this.pages.length > 0) {
        this.show("Loaded " + this.pages.length + " pages.", 2000,
            language + "-info");
    } else {
        this.show("Failed to load subtitles for language " + language, 4000,
            language + "-info");
    }

}

// Ugly hack independent of this scope (only its string will be injected).
function specialCode () {
    // Will hold the unix time minus local playback time (in milliseconds).
    $("body").append("

");

    // Determine how the target player will tell its current position
    // (in seconds).
    var getCurrentTime = function() {
        // Look for the regular player, subtitle editor (flash or html5)
        // and segment editor.
        if ("player" in window) {
            return player.getCurrentTime();
        }
        if ("html5player" in window) {
            return html5player.player.getCurrentTime();
        }
        if ("subber_player_flash_api" in window) {
            return subber_player_flash_api.player.currentTime();
        }
        if ("subber_player_html5_api" in window) {
            return subber_player_html5_api.player.currentTime();
        }
    }

    // Export this position continuously in html format (using the DOM).
    function syncForever() {
        var local = Math.round(getCurrentTime() * 1000);
        var syncVal = new Date().getTime() - local;
        document.getElementById("most-sync").innerHTML = syncVal;
        window.setTimeout(syncForever, 100);
    };

    syncForever();
}

function startSync() {
    // Inject the ugly hack into the web page.
    var script = document.createElement('script');
    script.appendChild(document.createTextNode(
        '('+ specialCode +')();'));
    (document.body || document.head || document.documentElement)
        .appendChild(script);
}

function updateBasicCSS() {
    // Find the positioning of the video player, or use (bad) defaults.
    var video = $("#video-player_html5_api")[0] || $("#subber_player")[0];
    var videoRect = video.getBoundingClientRect();

    // Configure the looks and position.
    //$("#most-overlay").removeAttr("style"); ?
    $("#most-overlay").css({
        position: "fixed",
        width: videoRect.width + "px",
        height: videoRect.height + "px",
        left: videoRect.left,
        top: videoRect.top,
        zIndex: "2147483647",
        pointerEvents: "none",
        textAlign: "center"
    });
}

function addOverlay(style) {
    // Create the overlay
    overlay = $("

");
    $("body").append(overlay);

    // Set the subtitle style
    updateBasicCSS();
    var styleEl = document.createElement("style");
    styleEl.innerHTML = style;
    $("head").append(styleEl);
}

// Convert an srt time string to integer.
function strToMs(loc) {
    var h, m, s, ms;
    [h, m, s, ms] = loc.replace(',', ':').split(':');
    [h, m, s, ms] = [parseInt(h), parseInt(m), parseInt(s), parseInt(ms)];
    return ((h*60 + m)*60 + s)*1000 + ms;
}

// Turn an srt into computation-friendly form: pages.
function parseSubtitles(srt) {
    if (srt === undefined || srt === "") return [];

    var pages = [];

    var srt2 = srt.replace(/\r\n|\r|\n/g, '\n')  // Normalize newlines.
                  .trim(srt)
                  .split('\n\n');

    // Parse each page.
    for (var i = 0; i < srt2.length; i++) {
        var a, b, c, d, e, loc0, loc1;
        a = srt2[i];
        [b, c] = [a.slice(0, a.indexOf("\n")), a.slice(a.indexOf("\n")+1)];
        [d, e] = [c.slice(0, c.indexOf("\n")), c.slice(c.indexOf("\n")+1)];
        [loc0, loc1] = d.split(" --> ");

        // The following line improves security but comes at the cost of
        // not supporting arbitrary html markup.
        e = e.replace(/<\/?[^>]+(>|$)/g, function(part) {
            var accept = ['', '', '
', '
', '', ''];
            for (var i = 0; i < accept.length; i++) {
                if (part === accept[i]) return part;
            }
            return "";
        });

        pages.push({'loc0': strToMs(loc0), 'loc1': strToMs(loc1), 's': e});
    }

    return pages;
}

<|endoftext|>


<commit_before>Package.describe({
  summary: "Adds famous dead people to the Meteor.users collection.",
  version: "1.5.7",
  name: "clinical:accounts-famous-dead-people",
  git: "http://github.com/awatson1978/accounts-famous-dead-people.git"
});

Package.on_use(function (api) {
  api.versionsFrom('1.0.3.1');
  api.use('meteor-platform');
  api.use('ecmascript@0.9.0');
  api.use('accounts-base@1.1.3');
  api.use('accounts-password@1.0.5');

  // api.use('clinical:hl7-resource-patient@3.4.6');

  api.addFiles('avatars/ada.lovelace.jpg', "client", {isAsset: true});
  api.addFiles('avatars/alan.turing.jpg', "client", {isAsset: true});
  api.addFiles('avatars/alexis.carrel.jpg', "client", {isAsset: true});
  api.addFiles('avatars/edward.doisy.jpg', "client", {isAsset: true});
  api.addFiles('avatars/edward.tatum.jpg', "client", {isAsset: true});
  api.addFiles('avatars/florence.nightingale.jpg', "client", {isAsset: true});
  api.addFiles('avatars/galen.jpg', "client", {isAsset: true});
  api.addFiles('avatars/george.beadle.jpg', "client", {isAsset: true});
  api.addFiles('avatars/george.wald.jpg', "client", {isAsset: true});
  api.addFiles('avatars/george.washington.carver.jpg', "client", {isAsset: true});
  api.addFiles('avatars/hermann.muller.jpg', "client", {isAsset: true});
  api.addFiles('avatars/hygea.jpg', "client", {isAsset: true});
  api.addFiles('avatars/james.watson.jpg', "client", {isAsset: true});
  api.addFiles('avatars/john.eccles.jpg', "client", {isAsset: true});
  api.addFiles('avatars/kurt.vonnegut.jpg', "client", {isAsset: true});
  api.addFiles('avatars/madam.curie.jpg', "client", {isAsset: true});
  api.addFiles('avatars/mary.shelley.jpg', "client", {isAsset: true});
  api.addFiles('avatars/mother.teresa.jpg', "client", {isAsset: true});
  api.addFiles('avatars/octavia.butler.jpg', "client", {isAsset: true});
  api.addFiles('avatars/roger.sperry.jpg', "client", {isAsset: true});
  api.addFiles('avatars/samuel.clemens.jpg', "client", {isAsset: true});

  api.addFiles('avatars/rosalind.franklin.jpg', "client", {isAsset: true});
  api.addFiles('avatars/nicola.tesla.jpg', "client", {isAsset: true});
  api.addFiles('avatars/grace.hopper.jpg', "client", {isAsset: true});
  api.addFiles('avatars/carl.gauss.jpg', "client", {isAsset: true});
  api.addFiles('avatars/claude.shannon.jpg', "client", {isAsset: true});
  api.addFiles('avatars/barbara.mcclintock.jpg', "client", {isAsset: true});
  api.addFiles('avatars/gertrude.elion.jpg', "client", {isAsset: true});
  api.addFiles('avatars/rachel.carson.jpg', "client", {isAsset: true});
  api.addFiles('avatars/maria.mayer.jpg', "client", {isAsset: true});
  api.addFiles('avatars/rita.levi.montalcini.jpg', "client", {isAsset: true});
  api.addFiles('avatars/elizabeth.blackwell.jpg', "client", {isAsset: true});

  api.addFiles('server/initialize.js', 'server');

  api.addFiles('lib/Characters.js');

  api.export('Characters');

});
<commit_msg>lodash<commit_after>Package.describe({
  summary: "Adds famous dead people to the Meteor.users collection.",
  version: "1.5.7",
  name: "clinical:accounts-famous-dead-people",
  git: "http://github.com/awatson1978/accounts-famous-dead-people.git"
});

Package.on_use(function (api) {
  api.versionsFrom('1.0.3.1');
  api.use('meteor-platform');
  api.use('ecmascript@0.9.0');
  api.use('accounts-base@1.1.3');
  api.use('accounts-password@1.0.5');

  // api.use('clinical:hl7-resource-patient@3.4.6');

  api.addFiles('avatars/ada.lovelace.jpg', "client", {isAsset: true});
  api.addFiles('avatars/alan.turing.jpg', "client", {isAsset: true});
  api.addFiles('avatars/alexis.carrel.jpg', "client", {isAsset: true});
  api.addFiles('avatars/edward.doisy.jpg', "client", {isAsset: true});
  api.addFiles('avatars/edward.tatum.jpg', "client", {isAsset: true});
  api.addFiles('avatars/florence.nightingale.jpg', "client", {isAsset: true});
  api.addFiles('avatars/galen.jpg', "client", {isAsset: true});
  api.addFiles('avatars/george.beadle.jpg', "client", {isAsset: true});
  api.addFiles('avatars/george.wald.jpg', "client", {isAsset: true});
  api.addFiles('avatars/george.washington.carver.jpg', "client", {isAsset: true});
  api.addFiles('avatars/hermann.muller.jpg', "client", {isAsset: true});
  api.addFiles('avatars/hygea.jpg', "client", {isAsset: true});
  api.addFiles('avatars/james.watson.jpg', "client", {isAsset: true});
  api.addFiles('avatars/john.eccles.jpg', "client", {isAsset: true});
  api.addFiles('avatars/kurt.vonnegut.jpg', "client", {isAsset: true});
  api.addFiles('avatars/madam.curie.jpg', "client", {isAsset: true});
  api.addFiles('avatars/mary.shelley.jpg', "client", {isAsset: true});
  api.addFiles('avatars/mother.teresa.jpg', "client", {isAsset: true});
  api.addFiles('avatars/octavia.butler.jpg', "client", {isAsset: true});
  api.addFiles('avatars/roger.sperry.jpg', "client", {isAsset: true});
  api.addFiles('avatars/samuel.clemens.jpg', "client", {isAsset: true});

  api.addFiles('avatars/rosalind.franklin.jpg', "client", {isAsset: true});
  api.addFiles('avatars/nicola.tesla.jpg', "client", {isAsset: true});
  api.addFiles('avatars/grace.hopper.jpg', "client", {isAsset: true});
  api.addFiles('avatars/carl.gauss.jpg', "client", {isAsset: true});
  api.addFiles('avatars/claude.shannon.jpg', "client", {isAsset: true});
  api.addFiles('avatars/barbara.mcclintock.jpg', "client", {isAsset: true});
  api.addFiles('avatars/gertrude.elion.jpg', "client", {isAsset: true});
  api.addFiles('avatars/rachel.carson.jpg', "client", {isAsset: true});
  api.addFiles('avatars/maria.mayer.jpg', "client", {isAsset: true});
  api.addFiles('avatars/rita.levi.montalcini.jpg', "client", {isAsset: true});
  api.addFiles('avatars/elizabeth.blackwell.jpg', "client", {isAsset: true});

  api.addFiles('server/initialize.js', 'server');

  api.addFiles('lib/Characters.js');

  api.export('Characters');

});

Npm.depends({
  "moment": "2.22.2",
  "lodash": "4.17.4"
})
<|endoftext|>


<commit_before>Package.describe({
  name: "jorjordandan:ionic",
  summary: "Ionic components for Meteor. No Angular!",
  version: "0.1.19",
  git: "https://github.com/meteoric/meteor-ionic.git"
});

Cordova.depends({
  'com.ionic.keyboard': '1.0.4'
});

Package.onUse(function(api) {
  api.versionsFrom("1.0");
  api.use([
    "templating",
    "underscore",
    "fastclick",
    "iron:router@1.0.0",
    "tracker",
    "session",
    "jquery"
  ], "client");

  api.addFiles([
    "vendor/snap.js",
    "vendor/snap.css",
    "vendor/slick.js",
    "vendor/slick.css"
  ], "client");

  api.addFiles([
    "components/ionActionSheet/ionActionSheet.html",
    "components/ionActionSheet/ionActionSheet.js",

    "components/ionBackdrop/ionBackdrop.html",
    "components/ionBackdrop/ionBackdrop.js",

    "components/ionBody/ionBody.html",
    "components/ionBody/ionBody.js",

    "components/ionContent/ionContent.html",
    "components/ionContent/ionContent.js",

    "components/ionFooterBar/ionFooterBar.html",
    "components/ionFooterBar/ionFooterBar.js",

    "components/ionHeaderBar/ionHeaderBar.html",
    "components/ionHeaderBar/ionHeaderBar.js",

    "components/ionIcon/ionIcon.html",
    "components/ionIcon/ionIcon.js",

    "components/ionItem/ionItem.html",
    "components/ionItem/ionItem.js",

    "components/ionKeyboard/ionKeyboard.js",
    "components/ionKeyboard/ionInputFocus.js",

    "components/ionList/ionList.html",
    "components/ionList/ionList.js",

    "components/ionLoading/ionLoading.html",
    "components/ionLoading/ionLoading.js",

    "components/ionModal/ionModal.html",
    "components/ionModal/ionModal.js",

    "components/ionNavBar/ionNavBar.html",
    "components/ionNavBar/ionNavBar.js",

    "components/ionNavBackButton/ionNavBackButton.html",
    "components/ionNavBackButton/ionNavBackButton.js",

    "components/ionNavView/ionNavView.html",
    "components/ionNavView/ionNavView.js",

    "components/ionPane/ionPane.html",
    "components/ionPane/ionPane.js",

    "components/ionPopover/ionPopover.html",
    "components/ionPopover/ionPopover.js",

    "components/ionPopup/ionPopup.html",
    "components/ionPopup/ionPopup.js",

    "components/ionRadio/ionRadio.html",
    "components/ionRadio/ionRadio.js",

    "components/ionSideMenu/ionSideMenu.html",
    "components/ionSideMenu/ionSideMenu.js",

    "components/ionSideMenuContainer/ionSideMenuContainer.html",
    "components/ionSideMenuContainer/ionSideMenuContainer.js",

    "components/ionSideMenuContent/ionSideMenuContent.html",
    "components/ionSideMenuContent/ionSideMenuContent.js",

    "components/ionSideMenus/ionSideMenus.html",
    "components/ionSideMenus/ionSideMenus.js",

    "components/ionSlideBox/ionSlideBox.html",
    "components/ionSlideBox/ionSlideBox.js",

    "components/ionSlide/ionSlide.html",
    "components/ionSlide/ionSlide.js",

    "components/ionSubfooterBar/ionSubfooterBar.html",
    "components/ionSubfooterBar/ionSubfooterBar.js",

    "components/ionSubheaderBar/ionSubheaderBar.html",
    "components/ionSubheaderBar/ionSubheaderBar.js",

    "components/ionTabs/ionTabs.html",
    "components/ionTabs/ionTabs.js",

    "components/ionTab/ionTab.html",
    "components/ionTab/ionTab.js",

    "components/ionView/ionView.html",
    "components/ionView/ionView.js"

  ], "client");

  api.export("Platform");

  api.export("IonActionSheet");
  api.export("IonBackdrop");
  api.export("IonHeaderBar");
  api.export("IonKeyboard");
  api.export("IonLoading");
  api.export("IonModal");
  api.export("IonNavigation");
  api.export("IonPopover");
  api.export("IonPopup");
  api.export("IonSideMenu");
});
<commit_msg>revert package file<commit_after>Package.describe({
  name: "meteoric:ionic",
  summary: "Ionic components for Meteor. No Angular!",
  version: "0.1.19",
  git: "https://github.com/meteoric/meteor-ionic.git"
});

Cordova.depends({
  'com.ionic.keyboard': '1.0.4'
});

Package.onUse(function(api) {
  api.versionsFrom("1.0");
  api.use([
    "templating",
    "underscore",
    "fastclick",
    "iron:router@1.0.0",
    "tracker",
    "session",
    "jquery"
  ], "client");

  api.addFiles([
    "vendor/snap.js",
    "vendor/snap.css",
    "vendor/slick.js",
    "vendor/slick.css"
  ], "client");

  api.addFiles([
    "components/ionActionSheet/ionActionSheet.html",
    "components/ionActionSheet/ionActionSheet.js",

    "components/ionBackdrop/ionBackdrop.html",
    "components/ionBackdrop/ionBackdrop.js",

    "components/ionBody/ionBody.html",
    "components/ionBody/ionBody.js",

    "components/ionContent/ionContent.html",
    "components/ionContent/ionContent.js",

    "components/ionFooterBar/ionFooterBar.html",
    "components/ionFooterBar/ionFooterBar.js",

    "components/ionHeaderBar/ionHeaderBar.html",
    "components/ionHeaderBar/ionHeaderBar.js",

    "components/ionIcon/ionIcon.html",
    "components/ionIcon/ionIcon.js",

    "components/ionItem/ionItem.html",
    "components/ionItem/ionItem.js",

    "components/ionKeyboard/ionKeyboard.js",
    "components/ionKeyboard/ionInputFocus.js",

    "components/ionList/ionList.html",
    "components/ionList/ionList.js",

    "components/ionLoading/ionLoading.html",
    "components/ionLoading/ionLoading.js",

    "components/ionModal/ionModal.html",
    "components/ionModal/ionModal.js",

    "components/ionNavBar/ionNavBar.html",
    "components/ionNavBar/ionNavBar.js",

    "components/ionNavBackButton/ionNavBackButton.html",
    "components/ionNavBackButton/ionNavBackButton.js",

    "components/ionNavView/ionNavView.html",
    "components/ionNavView/ionNavView.js",

    "components/ionPane/ionPane.html",
    "components/ionPane/ionPane.js",

    "components/ionPopover/ionPopover.html",
    "components/ionPopover/ionPopover.js",

    "components/ionPopup/ionPopup.html",
    "components/ionPopup/ionPopup.js",

    "components/ionRadio/ionRadio.html",
    "components/ionRadio/ionRadio.js",

    "components/ionSideMenu/ionSideMenu.html",
    "components/ionSideMenu/ionSideMenu.js",

    "components/ionSideMenuContainer/ionSideMenuContainer.html",
    "components/ionSideMenuContainer/ionSideMenuContainer.js",

    "components/ionSideMenuContent/ionSideMenuContent.html",
    "components/ionSideMenuContent/ionSideMenuContent.js",

    "components/ionSideMenus/ionSideMenus.html",
    "components/ionSideMenus/ionSideMenus.js",

    "components/ionSlideBox/ionSlideBox.html",
    "components/ionSlideBox/ionSlideBox.js",

    "components/ionSlide/ionSlide.html",
    "components/ionSlide/ionSlide.js",

    "components/ionSubfooterBar/ionSubfooterBar.html",
    "components/ionSubfooterBar/ionSubfooterBar.js",

    "components/ionSubheaderBar/ionSubheaderBar.html",
    "components/ionSubheaderBar/ionSubheaderBar.js",

    "components/ionTabs/ionTabs.html",
    "components/ionTabs/ionTabs.js",

    "components/ionTab/ionTab.html",
    "components/ionTab/ionTab.js",

    "components/ionView/ionView.html",
    "components/ionView/ionView.js"

  ], "client");

  api.export("Platform");

  api.export("IonActionSheet");
  api.export("IonBackdrop");
  api.export("IonHeaderBar");
  api.export("IonKeyboard");
  api.export("IonLoading");
  api.export("IonModal");
  api.export("IonNavigation");
  api.export("IonPopover");
  api.export("IonPopup");
  api.export("IonSideMenu");
});
<|endoftext|>


<commit_before>Package.describe({
  name: "froala:editor",
  summary: "A beautiful Javascript WYSIWYG HTML rich text editor.",
  version: "3.0.0",
  git: "https://github.com/froala/meteor-froala/"
});

Package.onUse(function(api) {
  api.use([

  ], "client");

  api.addFiles([
    // CSS
    "assets/css/froala_editor.min.css",
    "assets/css/froala_style.min.css",

    // CSS Plugins
    "assets/css/plugins/char_counter.min.css",
    "assets/css/plugins/code_view.min.css",
    "assets/css/plugins/colors.min.css",
    "assets/css/plugins/draggable.min.css",
    "assets/css/plugins/emoticons.min.css",
    "assets/css/plugins/file.min.css",
    "assets/css/plugins/fullscreen.min.css",
    "assets/css/plugins/help.min.css",
    "assets/css/plugins/image_manager.min.css",
    "assets/css/plugins/image.min.css",
    "assets/css/plugins/line_breaker.min.css",
    "assets/css/plugins/quick_insert.min.css",
    "assets/css/plugins/special_characters.min.css",
    "assets/css/plugins/table.min.css",
    "assets/css/plugins/video.min.css",

    // 3rd Party
    "assets/css/third_party/embedly.min.css",
    "assets/css/third_party/spell_checker.min.css",
    "assets/css/third_party/image_tui.min.css",
    "assets/css/third_party/font_awesome.min.css",

    // JS
    "assets/js/froala_editor.min.js",

    // JS Plugins
    "assets/js/plugins/align.min.js",
    "assets/js/plugins/char_counter.min.js",
    "assets/js/plugins/code_beautifier.min.js",
    "assets/js/plugins/code_view.min.js",
    "assets/js/plugins/colors.min.js",
    "assets/js/plugins/draggable.min.js",
    "assets/js/plugins/emoticons.min.js",
    "assets/js/plugins/entities.min.js",
    "assets/js/plugins/file.min.js",
    "assets/js/plugins/font_family.min.js",
    "assets/js/plugins/font_size.min.js",
    "assets/js/plugins/fullscreen.min.js",
    "assets/js/plugins/help.min.js",
    "assets/js/plugins/image.min.js",
    "assets/js/plugins/image_manager.min.js",
    "assets/js/plugins/inline_class.min.js",
    "assets/js/plugins/inline_style.min.js",
    "assets/js/plugins/line_breaker.min.js",
    "assets/js/plugins/line_height.min.js",
    "assets/js/plugins/link.min.js",
    "assets/js/plugins/lists.min.js",
    "assets/js/plugins/paragraph_format.min.js",
    "assets/js/plugins/paragraph_style.min.js",
    "assets/js/plugins/print.min.js",
    "assets/js/plugins/quick_insert.min.js",
    "assets/js/plugins/quote.min.js",
    "assets/js/plugins/save.min.js",
    "assets/js/plugins/special_characters.min.js",
    "assets/js/plugins/table.min.js",
    "assets/js/plugins/url.min.js",
    "assets/js/plugins/video.min.js",
    "assets/js/plugins/word_paste.min.js",

    // 3rd Party
    "assets/js/third_party/embedly.min.js",
    "assets/js/third_party/font_awesome.min.js",
    "assets/js/third_party/image_tui.min.js",
    "assets/js/third_party/spell_checker.min.js"
  ], ["client"]);
});
<commit_msg>Changing 3.0.0 to 3.0.0-beta<commit_after>Package.describe({
  name: "froala:editor",
  summary: "A beautiful Javascript WYSIWYG HTML rich text editor.",
  version: "3.0.0-beta",
  git: "https://github.com/froala/meteor-froala/"
});

Package.onUse(function(api) {
  api.use([

  ], "client");

  api.addFiles([
    // CSS
    "assets/css/froala_editor.min.css",
    "assets/css/froala_style.min.css",

    // CSS Plugins
    "assets/css/plugins/char_counter.min.css",
    "assets/css/plugins/code_view.min.css",
    "assets/css/plugins/colors.min.css",
    "assets/css/plugins/draggable.min.css",
    "assets/css/plugins/emoticons.min.css",
    "assets/css/plugins/file.min.css",
    "assets/css/plugins/fullscreen.min.css",
    "assets/css/plugins/help.min.css",
    "assets/css/plugins/image_manager.min.css",
    "assets/css/plugins/image.min.css",
    "assets/css/plugins/line_breaker.min.css",
    "assets/css/plugins/quick_insert.min.css",
    "assets/css/plugins/special_characters.min.css",
    "assets/css/plugins/table.min.css",
    "assets/css/plugins/video.min.css",

    // 3rd Party
    "assets/css/third_party/embedly.min.css",
    "assets/css/third_party/spell_checker.min.css",
    "assets/css/third_party/image_tui.min.css",
    "assets/css/third_party/font_awesome.min.css",

    // JS
    "assets/js/froala_editor.min.js",

    // JS Plugins
    "assets/js/plugins/align.min.js",
    "assets/js/plugins/char_counter.min.js",
    "assets/js/plugins/code_beautifier.min.js",
    "assets/js/plugins/code_view.min.js",
    "assets/js/plugins/colors.min.js",
    "assets/js/plugins/draggable.min.js",
    "assets/js/plugins/emoticons.min.js",
    "assets/js/plugins/entities.min.js",
    "assets/js/plugins/file.min.js",
    "assets/js/plugins/font_family.min.js",
    "assets/js/plugins/font_size.min.js",
    "assets/js/plugins/fullscreen.min.js",
    "assets/js/plugins/help.min.js",
    "assets/js/plugins/image.min.js",
    "assets/js/plugins/image_manager.min.js",
    "assets/js/plugins/inline_class.min.js",
    "assets/js/plugins/inline_style.min.js",
    "assets/js/plugins/line_breaker.min.js",
    "assets/js/plugins/line_height.min.js",
    "assets/js/plugins/link.min.js",
    "assets/js/plugins/lists.min.js",
    "assets/js/plugins/paragraph_format.min.js",
    "assets/js/plugins/paragraph_style.min.js",
    "assets/js/plugins/print.min.js",
    "assets/js/plugins/quick_insert.min.js",
    "assets/js/plugins/quote.min.js",
    "assets/js/plugins/save.min.js",
    "assets/js/plugins/special_characters.min.js",
    "assets/js/plugins/table.min.js",
    "assets/js/plugins/url.min.js",
    "assets/js/plugins/video.min.js",
    "assets/js/plugins/word_paste.min.js",

    // 3rd Party
    "assets/js/third_party/embedly.min.js",
    "assets/js/third_party/font_awesome.min.js",
    "assets/js/third_party/image_tui.min.js",
    "assets/js/third_party/spell_checker.min.js"
  ], ["client"]);
});
<|endoftext|>


<commit_before>Package.describe({
  name: 'percolate:google-api',
  summary: "A Meteor library to interact with Google's API",
  version: '1.0.1',
  git: 'https://github.com/percolatestudio/meteor-google-api'
});

Package.on_use(function (api, where) {
  if (api.versionsFrom) {
    api.versionsFrom('0.9.0');
    api.use(['http', 'livedata', 'google', 'mrt:q@1.0.1', 'accounts-base', 'underscore']);
  } else {
    api.use(['http', 'livedata', 'google', 'q', 'accounts-base', 'underscore']);
  }

  api.add_files(['utils.js', 'google-api-async.js'], ['client', 'server']);
  api.add_files(['google-api-methods.js'], ['server']);

  api.export('GoogleApi', ['client', 'server']);
});

Package.on_test(function (api) {
  api.use(['percolate:google-api', 'tinytest', 'http', 'accounts-base', 'service-configuration']);

  api.add_files('google-api-tests.js', ['client', 'server']);
});
<commit_msg>version change<commit_after>Package.describe({
  name: 'percolate:google-api',
  summary: "A Meteor library to interact with Google's API",
  version: '1.0.2',
  git: 'https://github.com/percolatestudio/meteor-google-api'
});

Package.on_use(function (api, where) {
  if (api.versionsFrom) {
    api.versionsFrom('0.9.0');
    api.use(['http', 'livedata', 'google', 'mrt:q@1.0.1', 'accounts-base', 'underscore']);
  } else {
    api.use(['http', 'livedata', 'google', 'q', 'accounts-base', 'underscore']);
  }

  api.add_files(['utils.js', 'google-api-async.js'], ['client', 'server']);
  api.add_files(['google-api-methods.js'], ['server']);

  api.export('GoogleApi', ['client', 'server']);
});

Package.on_test(function (api) {
  api.use(['percolate:google-api', 'tinytest', 'http', 'accounts-base', 'service-configuration']);

  api.add_files('google-api-tests.js', ['client', 'server']);
});
<|endoftext|>


<commit_before>
Package.describe({
  name: "zeroasterisk:throttle",
  summary: "A secure means of rate limiting, debounce even in a cluster (emails, etc)",
  version: "0.3.4",
  git: "https://github.com/zeroasterisk/Meteor-Throttle.git"
});

Package.onUse(function (api) {
  api.versionsFrom("0.9.0");
  api.use(['meteor', 'underscore', 'check'], 'server');
  // Export the object 'Throttle' to packages or apps that use this package.
  api.export('Throttle', 'server');
  api.addFiles('throttle.js', ['server']);
});

Package.onTest(function (api) {
  api.use("zeroasterisk:throttle");
  api.use('tinytest@1.0.0');
  api.addFiles('throttle_tests.js', ['client', 'server']);
});
<commit_msg>version bump<commit_after>
Package.describe({
  name: "zeroasterisk:throttle",
  summary: "A secure means of rate limiting, debounce even in a cluster (emails, etc)",
  version: "0.3.5",
  git: "https://github.com/zeroasterisk/Meteor-Throttle.git"
});

Package.onUse(function (api) {
  api.versionsFrom("0.9.0");
  api.use(['meteor', 'underscore', 'check'], 'server');
  // Export the object 'Throttle' to packages or apps that use this package.
  api.export('Throttle', 'server');
  api.addFiles('throttle.js', ['server']);
});

Package.onTest(function (api) {
  api.use("zeroasterisk:throttle");
  api.use('tinytest@1.0.0');
  api.addFiles('throttle_tests.js', ['client', 'server']);
});
<|endoftext|>


<commit_before>Package.describe({
  name: "pauli:linkedin",
  summary: "A OAuth2 wrapper for the LinkedIN API",
  version: "1.3.0",
  git: "https://github.com/PauliBuccini/meteor-linkedin.git"
});

Package.onUse(function(api) {
  api.versionsFrom('1.2.1')

  api.use('oauth2', ['client', 'server']);
  api.use('oauth', ['client', 'server']);
  api.use('http', ['server']);
  api.use('templating', 'client');
  api.use('underscore', 'server');
  api.use('random', 'client');
  api.use('service-configuration', ['client', 'server']);

  api.export('LinkedIn');

  api.addFiles(
    ['linkedin_configure.html', 'linkedin_configure.js'],
    'client');
  api.addFiles('linkedin_common.js', ['client', 'server']);
  api.addFiles('linkedin_server.js', 'server');
  api.addFiles('linkedin_client.js', 'client');
});

<commit_msg>git update<commit_after>Package.describe({
  name: "pauli:linkedin",
  summary: "A OAuth2 wrapper for the LinkedIN API",
  version: "1.3.1",
  git: "https://github.com/PoBuchi/meteor-linkedin.git"
});

Package.onUse(function(api) {
  api.versionsFrom('1.2.1')

  api.use('oauth2', ['client', 'server']);
  api.use('oauth', ['client', 'server']);
  api.use('http', ['server']);
  api.use('templating', 'client');
  api.use('underscore', 'server');
  api.use('random', 'client');
  api.use('service-configuration', ['client', 'server']);

  api.export('LinkedIn');

  api.addFiles(
    ['linkedin_configure.html', 'linkedin_configure.js'],
    'client');
  api.addFiles('linkedin_common.js', ['client', 'server']);
  api.addFiles('linkedin_server.js', 'server');
  api.addFiles('linkedin_client.js', 'client');
});

<|endoftext|>


<commit_before>/**
 * This file is referenced by the `dojoBuild` key in `package.json` and provides extra hinting specific to the Dojo
 * build system about how certain files in the package need to be handled at build time. Build profiles for the
 * application itself are stored in the `profiles` directory.
 */

var profile = {
	// Resource tags are functions that provide hints to the build system about the way files should be processed.
	// Each of these functions is called once for every file in the package directory. The first argument passed to
	// the function is the filename of the file, and the second argument is the computed AMD module ID of the file.
	resourceTags: {
		// Files that contain test code and should be excluded when the `copyTests` build flag exists and is `false`.
		// It is strongly recommended that the `mini` build flag be used instead of `copyTests`. Therefore, no files
		// are marked with the `test` tag here.
		test: function (filename, mid) {
			return false;
		},

		// Files that should be copied as-is without being modified by the build system.
		// All files in the `app/resources` directory that are not CSS files are marked as copy-only, since these files
		// are typically binaries (images, etc.) and may be corrupted by the build system if it attempts to process
		// them and naively assumes they are scripts.
		copyOnly: function (filename, mid) {
			return (/^cmv\/(images)\//.test(mid) && !/\.css$/.test(filename));
		},

		// Files that are AMD modules.
		// All JavaScript in this package should be AMD modules if you are starting a new project. If you are copying
		// any legacy scripts from an existing project, those legacy scripts should not be given the `amd` tag.
		amd: function (filename, mid) {
			return !this.copyOnly(filename, mid) && /\.js$/.test(filename);
		},

		// Files that should not be copied when the `mini` build flag is set to true.
		// In this case, we are excluding this package configuration file which is not necessary in a built copy of
		// the application.
		miniExclude: function (filename, mid) {
			return mid in {
				'cmv/package': 1
			};
		}
	}
};
<commit_msg>Exclude node_modules from amd search<commit_after>/**
 * This file is referenced by the `dojoBuild` key in `package.json` and provides extra hinting specific to the Dojo
 * build system about how certain files in the package need to be handled at build time. Build profiles for the
 * application itself are stored in the `profiles` directory.
 */

var profile = {
	// Resource tags are functions that provide hints to the build system about the way files should be processed.
	// Each of these functions is called once for every file in the package directory. The first argument passed to
	// the function is the filename of the file, and the second argument is the computed AMD module ID of the file.
	resourceTags: {
		// Files that contain test code and should be excluded when the `copyTests` build flag exists and is `false`.
		// It is strongly recommended that the `mini` build flag be used instead of `copyTests`. Therefore, no files
		// are marked with the `test` tag here.
		test: function (filename, mid) {
			return false;
		},

		// Files that should be copied as-is without being modified by the build system.
		// All files in the `app/resources` directory that are not CSS files are marked as copy-only, since these files
		// are typically binaries (images, etc.) and may be corrupted by the build system if it attempts to process
		// them and naively assumes they are scripts.
		copyOnly: function (filename, mid) {
			return (/^cmv\/(images)\//.test(mid) && !/\.css$/.test(filename)) ||
			/\/node_modules\//.test(mid);
		},

		// Files that are AMD modules.
		// All JavaScript in this package should be AMD modules if you are starting a new project. If you are copying
		// any legacy scripts from an existing project, those legacy scripts should not be given the `amd` tag.
		amd: function (filename, mid) {
			return !this.copyOnly(filename, mid) && /\.js$/.test(filename);
		},

		// Files that should not be copied when the `mini` build flag is set to true.
		// In this case, we are excluding this package configuration file which is not necessary in a built copy of
		// the application.
		miniExclude: function (filename, mid) {
			return mid in {
				'cmv/package': 1
			};
		}
	}
};
<|endoftext|>


<commit_before>Package.describe({
  summary: 'Adds images from House MD to the Meteor.users collection.',
  version: '2.3.3',
  name: 'clinical:accounts-housemd',
  git: 'http://github.com/awatson1978/accounts-housemd.git',
  isDebug: true
});

Package.on_use(function (api) {
  api.versionsFrom('1.1.0.3');
  api.use('meteor-platform');

  api.use('ecmascript@0.9.0');
  api.use('accounts-base@1.1.3');
  api.use('accounts-password@1.0.5');

  api.use('clinical:hl7-resource-practitioner@3.2.9');
  api.imply('clinical:hl7-resource-practitioner');

  api.addFiles('server/initialize.js', 'server');

  api.addFiles('housemd/allison.camron.jpg', 'client', {isAsset: true});
  api.addFiles('housemd/eric.foreman.jpg', 'client', {isAsset: true});
  api.addFiles('housemd/gregory.house.jpg', 'client', {isAsset: true});
  api.addFiles('housemd/lawrence.kutner.jpg', 'client', {isAsset: true});
  api.addFiles('housemd/lisa.cuddy.jpg', 'client', {isAsset: true});
  api.addFiles('housemd/robert.chase.jpg', 'client', {isAsset: true});
  api.addFiles('housemd/james.wilson.jpg', 'client', {isAsset: true});
  api.addFiles('housemd/thirteen.jpg', 'client', {isAsset: true});

  api.addFiles('lib/Characters.js');

  api.export('Characters');
});
<commit_msg>RC20 attempt<commit_after>Package.describe({
  summary: 'Adds images from House MD to the Meteor.users collection.',
  version: '2.3.4',
  name: 'clinical:accounts-housemd',
  git: 'http://github.com/awatson1978/accounts-housemd.git',
  isDebug: true
});

Package.on_use(function (api) {
  api.versionsFrom('1.1.0.3');
  api.use('meteor-platform');

  api.use('ecmascript@0.9.0');
  api.use('accounts-base@1.1.3');
  api.use('accounts-password@1.0.5');

  api.use('clinical:hl7-resource-practitioner@3.2.9');
  api.imply('clinical:hl7-resource-practitioner');

  api.addFiles('server/initialize.js', 'server');

  api.addFiles('housemd/allison.camron.jpg', 'client', {isAsset: true});
  api.addFiles('housemd/eric.foreman.jpg', 'client', {isAsset: true});
  api.addFiles('housemd/gregory.house.jpg', 'client', {isAsset: true});
  api.addFiles('housemd/lawrence.kutner.jpg', 'client', {isAsset: true});
  api.addFiles('housemd/lisa.cuddy.jpg', 'client', {isAsset: true});
  api.addFiles('housemd/robert.chase.jpg', 'client', {isAsset: true});
  api.addFiles('housemd/james.wilson.jpg', 'client', {isAsset: true});
  api.addFiles('housemd/thirteen.jpg', 'client', {isAsset: true});

  api.addFiles('lib/Characters.js');

  api.export('Characters');
});
<|endoftext|>


<commit_before>Package.describe({
  "name": "alethes:pages"
  "summary": "State of the art, out of the box Meteor pagination",
  "version": "1.1.3",
  "git": "https://github.com/alethes/meteor-pages"
});

Package.on_use(function(api){
    api.versionsFrom("METEOR@0.9.0")
    api.use([
        "deps",
        "underscore",
        "coffeescript"
    ]);

    api.use([
        "templating",
        "handlebars",
        "ui",
        "session"
    ], "client");

    api.use([
        "deps",
        "underscore",
        "coffeescript",
        "check"
    ], "server");

    api.add_files([
        "lib/pages.coffee"
    ]);

    api.add_files([
        "client/templates.html",
        "client/controllers.coffee",
        "client/main.css",
        "public/loader.gif"
    ], "client");
});

Package.on_test(function(api){
    api.versionsFrom("METEOR@0.9.0")
    api.use([
        "deps",
        "underscore",
        "coffeescript"
    ]);

    api.use([
        "templating",
        "handlebars",
        "ui",
        "session"
    ], "client");

    api.use([
        "deps",
        "underscore",
        "coffeescript",
        "check"
    ], "server");

    api.add_files([
        "lib/pages.coffee",
        "test/tests.coffee"
    ]);

    api.add_files([
        "client/templates.html",
        "client/controllers.coffee",
        "client/main.css",
        "public/loader.gif"
    ], "client");
});
<commit_msg>Update package.js<commit_after>Package.describe({
  "name": "alethes:pages",
  "summary": "State of the art, out of the box Meteor pagination",
  "version": "1.1.3",
  "git": "https://github.com/alethes/meteor-pages"
});

Package.on_use(function(api){
    api.versionsFrom("METEOR@0.9.2.2")
    api.use([
        "deps",
        "underscore",
        "coffeescript"
    ]);

    api.use([
        "templating",
        "handlebars",
        "ui",
        "session"
    ], "client");

    api.use([
        "deps",
        "underscore",
        "coffeescript",
        "check"
    ], "server");

    api.add_files([
        "lib/pages.coffee"
    ]);

    api.add_files([
        "client/templates.html",
        "client/controllers.coffee",
        "client/main.css",
        "public/loader.gif"
    ], "client");
});

Package.on_test(function(api){
    api.versionsFrom("METEOR@0.9.0")
    api.use([
        "deps",
        "underscore",
        "coffeescript"
    ]);

    api.use([
        "templating",
        "handlebars",
        "ui",
        "session"
    ], "client");

    api.use([
        "deps",
        "underscore",
        "coffeescript",
        "check"
    ], "server");

    api.add_files([
        "lib/pages.coffee",
        "test/tests.coffee"
    ]);

    api.add_files([
        "client/templates.html",
        "client/controllers.coffee",
        "client/main.css",
        "public/loader.gif"
    ], "client");
});
<|endoftext|>


<commit_before>Package.describe({
  name: 'timbrandin:sideburns',
  version: '0.2.0',
  // Brief, one-line summary of the package.
  summary: 'React templates for Meteor',
  // URL to the Git repository containing the source code for this package.
  git: 'https://github.com/timbrandin/meteor-react-sideburns',
  // By default, Meteor will default to using README.md for documentation.
  // To avoid submitting documentation, set this field to null.
  documentation: 'README.md'
});

Package.registerBuildPlugin({
  name: 'transpileJSXHTML',
  use: [
    'underscore',
    'babel-compiler@5.4.7',
    'cosmos:browserify@0.4.0',
  ],
  sources: [
    'react-events.js',
    'sideburns.js'
  ],
  npmDependencies: {
    'cheerio': '0.7.0',
    'eval': '0.1.0'
  }
});

Npm.depends({
  "classnames": "2.1.3"
});

Package.onUse(function (api) {
  // We need the Babel helpers as a run-time dependency of the generated code.
  api.imply('babel-runtime@0.1.0');
  api.imply('react-meteor-data@0.1.0');
  api.use(['cosmos:browserify@0.4.0'], 'client');

  api.addFiles([
    'classnames-server.js',
    'sideburns-export.js'
  ], 'server');

  api.addFiles([
    'client.browserify.js',
    'sideburns-export.js'
  ], 'client');

  api.export('Sideburns');
});
<commit_msg>Added missing version constraint.<commit_after>Package.describe({
  name: 'timbrandin:sideburns',
  version: '0.2.0',
  // Brief, one-line summary of the package.
  summary: 'React templates for Meteor',
  // URL to the Git repository containing the source code for this package.
  git: 'https://github.com/timbrandin/meteor-react-sideburns',
  // By default, Meteor will default to using README.md for documentation.
  // To avoid submitting documentation, set this field to null.
  documentation: 'README.md'
});

Package.registerBuildPlugin({
  name: 'transpileJSXHTML',
  use: [
    'underscore@1.0.3',
    'babel-compiler@5.4.7',
    'cosmos:browserify@0.4.0',
  ],
  sources: [
    'react-events.js',
    'sideburns.js'
  ],
  npmDependencies: {
    'cheerio': '0.7.0',
    'eval': '0.1.0'
  }
});

Npm.depends({
  "classnames": "2.1.3"
});

Package.onUse(function (api) {
  // We need the Babel helpers as a run-time dependency of the generated code.
  api.imply('babel-runtime@0.1.0');
  api.imply('react-meteor-data@0.1.0');
  api.use(['cosmos:browserify@0.4.0'], 'client');

  api.addFiles([
    'classnames-server.js',
    'sideburns-export.js'
  ], 'server');

  api.addFiles([
    'client.browserify.js',
    'sideburns-export.js'
  ], 'client');

  api.export('Sideburns');
});
<|endoftext|>


<commit_before>Package.describe({
  name: 'robodo:include-local-packages',
  summary: 'Include private packages in your meteor app `packages` folder.',
  version: '0.0.2',
  git: 'https://github.com/orangewise/robodo-include-local-packages',
  debugOnly: true
});

Npm.depends({
  'watch': '0.16.0',
  'fs-extra': '0.18.4'
});

Package.onUse(function(api) {
  api.versionsFrom('1.0');
  api.addFiles('watch.js', 'server');
});
<commit_msg>always refresh package.js<commit_after>Package.describe({
  name: 'robodo:include-local-packages',
  summary: 'Include private packages in your meteor app `packages` folder.',
  version: '0.0.3',
  git: 'https://github.com/orangewise/robodo-include-local-packages',
  debugOnly: true
});

Npm.depends({
  'watch': '0.16.0',
  'fs-extra': '0.18.4'
});

Package.onUse(function(api) {
  api.versionsFrom('1.0');
  api.addFiles('watch.js', 'server');
});
<|endoftext|>


<commit_before>Package.describe({
  summary: "Vertical Calendar plugin for meteor ",
  version: "0.0.1-rc.2",
  git: "https://github.com/jimmiebtlr/vertiCal.git"
});

Package.onUse(function(api) {
  api.use(['templating'],'client');
  api.versionsFrom('METEOR@0.9.1.1');
  api.addFiles(['vertiCal.html','vertiCal.js','vertiCal.css'],'client');
});
<commit_msg>Bump version.<commit_after>Package.describe({
  summary: "Vertical Calendar plugin for meteor ",
  version: "0.0.1-rc.3",
  git: "https://github.com/jimmiebtlr/vertiCal.git"
});

Package.onUse(function(api) {
  api.use(['templating'],'client');
  api.versionsFrom('METEOR@0.9.1.1');
  api.addFiles(['vertiCal.html','vertiCal.js','vertiCal.css'],'client');
});
<|endoftext|>


<commit_before>Package.describe({
  name: 'arkham:comments-ui',
  summary: 'Simple templates for disqus-like comment functionality',
  version: '1.3.3',
  git: 'https://github.com/komentify/meteor-comments-ui.git'
});

Npm.depends({
  linkifyjs: '2.0.2',
});

Package.onUse(function(api) {
  // Meteor Version
  api.versionsFrom('METEOR@1.4.1.2');

  // Meteor Core Dependencies
  api.use(['accounts-password@1.0.1'], { weak: true });
  api.use([
    'accounts-base',
    'ecmascript',
    'underscore',
    'mongo-livedata',
    'templating',
    'jquery',
    'check',
    'less@2.5.0_2',
    'tracker',
    'check',
    'random',
    'markdown',
    'reactive-dict',
  ]);

  // Atmosphere Package Dependencies
  api.use([
    'aldeed:collection2@2.5.0', 'aldeed:simple-schema@1.3.3', 'dburles:collection-helpers@1.0.3',
    'momentjs:moment@2.10.6', 'reywood:publish-composite@1.4.2',
    'aldeed:template-extension@4.0.0', 'barbatus:stars-rating@1.0.7'
  ]);

  // Package specific globals and files
  api.addFiles([
    'lib/collections/anonymous-user.js',
    'lib/collections/comments.js',
    'lib/collections/methods/anonymous-user.js',
    'lib/collections/methods/comments.js',
  ]);

  api.addFiles([
    'lib/services/media-analyzers/image.js',
    'lib/services/media-analyzers/youtube.js',
    'lib/services/user.js',
    'lib/services/time-tick.js',
    'lib/services/media.js',
    'lib/components/commentsBox/commentsBox.html',
    'lib/components/commentsBox/commentsBox.less',
    'lib/components/commentsSingleComment/commentsSingleComment.html',
    'lib/components/commentsTextarea/commentsTextarea.html',
    'lib/components/commentsSubheader/commentsSubheader.html',
    'lib/components/commentsList/commentsList.html',
    'lib/api.js',
  ]);

  api.addFiles([
    'lib/components/helpers.js',
    'lib/components/commentsBox/commentsBox.js',
    'lib/components/commentsSingleComment/commentsSingleComment.js',
    'lib/components/commentsTextarea/commentsTextarea.js',
    'lib/components/commentsSubheader/commentsSubheader.js',
    'lib/components/commentsList/commentsList.js',
  ], 'client');

  api.addFiles([
    'lib/server/publish.js',
    'lib/services/hashing.js',
    'lib/comment-status-api.js',
  ], 'server');

  api.export('Comments');
});

Package.onTest(function(api) {
  api.use(['tinytest', 'accounts-password', 'audit-argument-checks', 'check']);
  api.use('arkham:comments-ui');

  api.addFiles('tests/api-tests.js');
  api.addFiles('tests/ui-tests.js', 'client');
});
<commit_msg>Bump version 1.4.0<commit_after>Package.describe({
  name: 'arkham:comments-ui',
  summary: 'Simple templates for disqus-like comment functionality',
  version: '1.4.0',
  git: 'https://github.com/komentify/meteor-comments-ui.git'
});

Npm.depends({
  linkifyjs: '2.0.2',
});

Package.onUse(function(api) {
  // Meteor Version
  api.versionsFrom('METEOR@1.4.1.2');

  // Meteor Core Dependencies
  api.use(['accounts-password@1.0.1'], { weak: true });
  api.use([
    'accounts-base',
    'ecmascript',
    'underscore',
    'mongo-livedata',
    'templating',
    'jquery',
    'check',
    'less@2.5.0_2',
    'tracker',
    'check',
    'random',
    'markdown',
    'reactive-dict',
  ]);

  // Atmosphere Package Dependencies
  api.use([
    'aldeed:collection2@2.5.0', 'aldeed:simple-schema@1.3.3', 'dburles:collection-helpers@1.0.3',
    'momentjs:moment@2.10.6', 'reywood:publish-composite@1.4.2',
    'aldeed:template-extension@4.0.0', 'barbatus:stars-rating@1.0.7'
  ]);

  // Package specific globals and files
  api.addFiles([
    'lib/collections/anonymous-user.js',
    'lib/collections/comments.js',
    'lib/collections/methods/anonymous-user.js',
    'lib/collections/methods/comments.js',
  ]);

  api.addFiles([
    'lib/services/media-analyzers/image.js',
    'lib/services/media-analyzers/youtube.js',
    'lib/services/user.js',
    'lib/services/time-tick.js',
    'lib/services/media.js',
    'lib/components/commentsBox/commentsBox.html',
    'lib/components/commentsBox/commentsBox.less',
    'lib/components/commentsSingleComment/commentsSingleComment.html',
    'lib/components/commentsTextarea/commentsTextarea.html',
    'lib/components/commentsSubheader/commentsSubheader.html',
    'lib/components/commentsList/commentsList.html',
    'lib/api.js',
  ]);

  api.addFiles([
    'lib/components/helpers.js',
    'lib/components/commentsBox/commentsBox.js',
    'lib/components/commentsSingleComment/commentsSingleComment.js',
    'lib/components/commentsTextarea/commentsTextarea.js',
    'lib/components/commentsSubheader/commentsSubheader.js',
    'lib/components/commentsList/commentsList.js',
  ], 'client');

  api.addFiles([
    'lib/server/publish.js',
    'lib/services/hashing.js',
    'lib/comment-status-api.js',
  ], 'server');

  api.export('Comments');
});

Package.onTest(function(api) {
  api.use(['tinytest', 'accounts-password', 'audit-argument-checks', 'check']);
  api.use('arkham:comments-ui');

  api.addFiles('tests/api-tests.js');
  api.addFiles('tests/ui-tests.js', 'client');
});
<|endoftext|>