<commit_before>"use strict";
import React, { Component, PropTypes } from 'react';
import { Link } from 'react-router';
import moment from 'moment';
import { getCalendar, getItems } from '../../actions';
import PageHeader from '../../components/PageHeader';
import Button from '../../components/Button';
import Loading from '../../components/Loading';
import LeftIcon from '../../icons/Left';
import RightIcon from '../../icons/Right';
import PageContext from '../page/PageContext';

const LEFT_KEY = 37;
const RIGHT_KEY = 39;

export default class Calendar extends Component {

  constructor () {
    super();
    this._changeDate = this._changeDate.bind(this);
    this._onChangeMonth = this._onChangeMonth.bind(this);
    this._onChangeYear = this._onChangeYear.bind(this);
    this._onSearch = this._onSearch.bind(this);
    this._onFilter = this._onFilter.bind(this);
    this._onKeyDown = this._onKeyDown.bind(this);
    this.state = {
      activeCalendars: {},
      calendar: { events: [] },
      days: {},
      calendars: [],
      searchText: ''
    };
  }

  componentDidMount () {
    document.title = 'Calendar';

    this._load(this.props);

    // Load the possible calendars
    getItems('calendars', { select: 'name', sort: 'name' })
    .then(calendars => this.setState({ calendars: calendars }))
    .catch(error => console.log('!!! Calendar calendars catch', error));

    window.addEventListener("keydown", this._onKeyDown);
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.params.id !== this.props.params.id) {
      this._load(nextProps);
    }
  }

  componentWillUnmount () {
    window.removeEventListener("keydown", this._onKeyDown);
  }

  _load (props) {
    const { params: { id } } = this.props;
    const { activeCalendars, date, searchText } = this.state;
    this.setState({ loading: true });
    const ids = Object.keys(activeCalendars);
    getCalendar({ date, searchText, id, ids })
    .then(calendar => {

      // structure by day to make rendering more efficient
      let days = {};
      calendar.events.forEach(event => {
        const day = moment(event.start).startOf('day').valueOf();
        if (! days[day]) {
          days[day] = [];
        }
        days[day].push(event);
        if (event.dates) {
          event.dates.forEach(date => {
            const day2 = moment(date).startOf('day').valueOf();
            if (day2 !== day) {
              if (! days[day2]) {
                days[day2] = [];
              }
              days[day2].push(event);
            }
          });
        }
      });

      this.setState({ calendar: calendar, days: days, loading: false });
    })
    .catch(error => console.log('!!! Calendar get catch', error));
  }

  _throttledLoad () {
    // throttle gets when user is typing
    clearTimeout(this._getTimer);
    this._getTimer = setTimeout(this._load.bind(this, this.props), 100);
  }

  _changeDate (date) {
    return (event) => {
      this.setState({ date: date }, this._throttledLoad);
    };
  }

  _onChangeMonth (event) {
    let date = moment(this.state.date);
    date.month(event.target.value);
    this.setState({ date: date }, this._throttledLoad);
  }

  _onChangeYear (event) {
    let date = moment(this.state.date);
    date.year(event.target.value);
    this.setState({ date: date }, this._throttledLoad);
  }

  _onSearch (event) {
    const searchText = event.target.value;
    this.setState({ searchText: searchText }, this._throttledLoad);
  }

  _onFilter (event) {
    const value = event.target.value;
    const search = (value && 'All' !== value) ? `?name=${value}` : undefined;
    this.context.router.replace({ pathname: '/calendar', search: search });
  }

  _onKeyDown (event) {
    const { calendar } = this.state;
    const key = (event.keyCode ? event.keyCode : event.which);
    if (LEFT_KEY === key) {
      this.setState({ date: moment(calendar.previous) }, this._get);
    } else if (RIGHT_KEY === key) {
      this.setState({ date: moment(calendar.next) }, this._get);
    }
  }

  _toggleCalendar (id) {
    return () => {
      let nextActiveCalendars = { ...this.state.activeCalendars };
      if (nextActiveCalendars[id]) {
        delete nextActiveCalendars[id];
      } else {
        nextActiveCalendars[id] = true;
      }
      this.setState({ activeCalendars: nextActiveCalendars },
        this._throttledLoad);
    };
  }

  _renderDaysOfWeek () {
    const { calendar: { start } } = this.state;
    let result = [];
    let date = moment(start);
    while (result.length < 7) {
      const name = date.format('dddd');
      result.push(
{name}
);
      date = date.add(1, 'day');
    }
    return result;
  }

  _renderWeek (days, key) {
    return (

        {days}

    );
  }

  _renderEvents (date) {
    const { days } = this.state;
    const events = days[date.valueOf()];
    return (events || []).map(event => {
      const start = moment(event.start);
      let time;
      if (true || start.isSame(date, 'day')) {
        time = (

            {start.format('h:mm a')}

        );
      }

      return (

          <Link to={`/events/${event._id}`}>
            {time}
            {event.name}
          </Link>

      );
    });
  }

  _renderWeeks () {
    const { calendar } = this.state;
    let weeks = [];
    let today = moment();
    let focus = moment(calendar.date);
    let date = moment(calendar.start).startOf('day');
    let end = moment(calendar.end);
    let days, previous;

    while (date.isSameOrBefore(end)) {

      if (! previous || previous.isBefore(date, 'week')) {
        if (previous) {
          weeks.push(this._renderWeek(days, previous.valueOf()));
        }
        days = [];
      }
      const dayEvents = this._renderEvents(date, days);

      let classNames = ['calendar__day'];
      if (date.isSame(today, 'date')) {
        classNames.push('calendar__day--today');
      }
      if (date.month() !== focus.month()) {
        classNames.push('calendar__day--alternate');
      }

      days.push(

              {date.format('dddd')}

              {date.format('MMMM')}

            {date.format('D')}

            {dayEvents}

      );

      previous = moment(date);
      date.add(1, 'day');
    }
    if (previous) {
      weeks.push(this._renderWeek(days, previous.valueOf()));
    }

    return weeks;
  }

  _renderFilter () {
    const { activeCalendars, calendars } = this.state;
    const controls = calendars.map(calendar => (

        <input id={calendar._id} name={calendar._id} type="checkbox"
          checked={activeCalendars[calendar._id] || false}
          onChange={this._toggleCalendar(calendar._id)} />
        {calendar.name}

    ));
    controls.unshift(

        <input id="all-calendars" name="all-calendars" type="checkbox"
          checked={Object.keys(activeCalendars).length === 0}
          onChange={() => this.setState({ activeCalendars: {} },
            this._throttledLoad)} />
        All

    );
    return (

        {controls}

    );
  }

  render () {
    const { params: { id } } = this.props;
    const { calendar, filterActive, searchText, loading } = this.state;
    const date = moment(calendar.date);
    const daysOfWeek = this._renderDaysOfWeek();
    const weeks = loading ? undefined : this._renderWeeks();
    const loadingIndicator = loading ? <Loading /> : undefined;

    let filter;
    if (filterActive) {
      filter = this._renderFilter();
    }

    let actions = [];
    if (id) {
      actions.push(
        <Link key="add"
          to={`/events/add?calendarId=${encodeURIComponent(calendar._id)}`}
          className="a-header">Add</Link>
      );
      actions.push(
        <Link key="edit" to={`/calendars/${calendar._id}/edit`}
          className="a-header">Edit</Link>
      );
    } else {
      actions.push(

            <Button label="Calendars" className="button-header"
              onClick={() => this.setState({
                filterActive: ! this.state.filterActive})}/>
            {filter}

      );
    }

    let months = [];
    let monthDate = moment(date).startOf('year');
    while (monthDate.year() === date.year()) {
      months.push(
        <option key={monthDate.month()}>{monthDate.format('MMMM')}</option>
      );
      monthDate.add(1, 'month');
    }

    let years = [];
    let now = moment();
    let yearDate = moment().subtract(3, 'years');
    while (yearDate.year() <= (now.year() + 2)) {
      years.push(
        <option key={yearDate.year()}>{yearDate.format('YYYY')}</option>
      );
      yearDate.add(1, 'year');
    }

    let today;
    if (! date.isSame(now, 'date')) {
      today = Today;
    }

    return (

        <PageHeader title={calendar.name || 'Calendar'}
          homer={id ? false : true}
          back={id ? true : false}
          searchText={searchText} onSearch={this._onSearch}
          actions={actions} />

            <button type="button" className="button-icon"
              onClick={this._changeDate(moment(calendar.previous))}>
              <LeftIcon />
            </button>

              <select value={moment(calendar.date).format('MMMM')}
                onChange={this._onChangeMonth}>
                {months}
              </select>
              <select value={moment(calendar.date).format('YYYY')}
                onChange={this._onChangeYear}>
                {years}
              </select>
              {today}

            <button type="button" className="button-icon"
              onClick={this._changeDate(moment(calendar.next))}>
              <RightIcon />
            </button>

            {daysOfWeek}

          {weeks}
          {loadingIndicator}

        <PageContext
          filter={calendar ? { 'sections.calendarId': calendar._id }
            : undefined} />

    );
  }
};

Calendar.propTypes = {
  location: PropTypes.shape({
    query: PropTypes.object
  }),
  params: PropTypes.shape({
    id: PropTypes.string
  })
};

Calendar.contextTypes = {
  router: PropTypes.any
};
<commit_msg>Don't reference pages from main Calendar.<commit_after>"use strict";
import React, { Component, PropTypes } from 'react';
import { Link } from 'react-router';
import moment from 'moment';
import { getCalendar, getItems } from '../../actions';
import PageHeader from '../../components/PageHeader';
import Button from '../../components/Button';
import Loading from '../../components/Loading';
import LeftIcon from '../../icons/Left';
import RightIcon from '../../icons/Right';
import PageContext from '../page/PageContext';

const LEFT_KEY = 37;
const RIGHT_KEY = 39;

export default class Calendar extends Component {

  constructor () {
    super();
    this._changeDate = this._changeDate.bind(this);
    this._onChangeMonth = this._onChangeMonth.bind(this);
    this._onChangeYear = this._onChangeYear.bind(this);
    this._onSearch = this._onSearch.bind(this);
    this._onFilter = this._onFilter.bind(this);
    this._onKeyDown = this._onKeyDown.bind(this);
    this.state = {
      activeCalendars: {},
      calendar: { events: [] },
      days: {},
      calendars: [],
      searchText: ''
    };
  }

  componentDidMount () {
    document.title = 'Calendar';

    this._load(this.props);

    // Load the possible calendars
    getItems('calendars', { select: 'name', sort: 'name' })
    .then(calendars => this.setState({ calendars: calendars }))
    .catch(error => console.log('!!! Calendar calendars catch', error));

    window.addEventListener("keydown", this._onKeyDown);
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.params.id !== this.props.params.id) {
      this._load(nextProps);
    }
  }

  componentWillUnmount () {
    window.removeEventListener("keydown", this._onKeyDown);
  }

  _load (props) {
    const { params: { id } } = this.props;
    const { activeCalendars, date, searchText } = this.state;
    this.setState({ loading: true });
    const ids = Object.keys(activeCalendars);
    getCalendar({ date, searchText, id, ids })
    .then(calendar => {

      // structure by day to make rendering more efficient
      let days = {};
      calendar.events.forEach(event => {
        const day = moment(event.start).startOf('day').valueOf();
        if (! days[day]) {
          days[day] = [];
        }
        days[day].push(event);
        if (event.dates) {
          event.dates.forEach(date => {
            const day2 = moment(date).startOf('day').valueOf();
            if (day2 !== day) {
              if (! days[day2]) {
                days[day2] = [];
              }
              days[day2].push(event);
            }
          });
        }
      });

      this.setState({ calendar: calendar, days: days, loading: false });
    })
    .catch(error => console.log('!!! Calendar get catch', error));
  }

  _throttledLoad () {
    // throttle gets when user is typing
    clearTimeout(this._getTimer);
    this._getTimer = setTimeout(this._load.bind(this, this.props), 100);
  }

  _changeDate (date) {
    return (event) => {
      this.setState({ date: date }, this._throttledLoad);
    };
  }

  _onChangeMonth (event) {
    let date = moment(this.state.date);
    date.month(event.target.value);
    this.setState({ date: date }, this._throttledLoad);
  }

  _onChangeYear (event) {
    let date = moment(this.state.date);
    date.year(event.target.value);
    this.setState({ date: date }, this._throttledLoad);
  }

  _onSearch (event) {
    const searchText = event.target.value;
    this.setState({ searchText: searchText }, this._throttledLoad);
  }

  _onFilter (event) {
    const value = event.target.value;
    const search = (value && 'All' !== value) ? `?name=${value}` : undefined;
    this.context.router.replace({ pathname: '/calendar', search: search });
  }

  _onKeyDown (event) {
    const { calendar } = this.state;
    const key = (event.keyCode ? event.keyCode : event.which);
    if (LEFT_KEY === key) {
      this.setState({ date: moment(calendar.previous) }, this._get);
    } else if (RIGHT_KEY === key) {
      this.setState({ date: moment(calendar.next) }, this._get);
    }
  }

  _toggleCalendar (id) {
    return () => {
      let nextActiveCalendars = { ...this.state.activeCalendars };
      if (nextActiveCalendars[id]) {
        delete nextActiveCalendars[id];
      } else {
        nextActiveCalendars[id] = true;
      }
      this.setState({ activeCalendars: nextActiveCalendars },
        this._throttledLoad);
    };
  }

  _renderDaysOfWeek () {
    const { calendar: { start } } = this.state;
    let result = [];
    let date = moment(start);
    while (result.length < 7) {
      const name = date.format('dddd');
      result.push(
{name}
);
      date = date.add(1, 'day');
    }
    return result;
  }

  _renderWeek (days, key) {
    return (

        {days}

    );
  }

  _renderEvents (date) {
    const { days } = this.state;
    const events = days[date.valueOf()];
    return (events || []).map(event => {
      const start = moment(event.start);
      let time;
      if (true || start.isSame(date, 'day')) {
        time = (

            {start.format('h:mm a')}

        );
      }

      return (

          <Link to={`/events/${event._id}`}>
            {time}
            {event.name}
          </Link>

      );
    });
  }

  _renderWeeks () {
    const { calendar } = this.state;
    let weeks = [];
    let today = moment();
    let focus = moment(calendar.date);
    let date = moment(calendar.start).startOf('day');
    let end = moment(calendar.end);
    let days, previous;

    while (date.isSameOrBefore(end)) {

      if (! previous || previous.isBefore(date, 'week')) {
        if (previous) {
          weeks.push(this._renderWeek(days, previous.valueOf()));
        }
        days = [];
      }
      const dayEvents = this._renderEvents(date, days);

      let classNames = ['calendar__day'];
      if (date.isSame(today, 'date')) {
        classNames.push('calendar__day--today');
      }
      if (date.month() !== focus.month()) {
        classNames.push('calendar__day--alternate');
      }

      days.push(

              {date.format('dddd')}

              {date.format('MMMM')}

            {date.format('D')}

            {dayEvents}

      );

      previous = moment(date);
      date.add(1, 'day');
    }
    if (previous) {
      weeks.push(this._renderWeek(days, previous.valueOf()));
    }

    return weeks;
  }

  _renderFilter () {
    const { activeCalendars, calendars } = this.state;
    const controls = calendars.map(calendar => (

        <input id={calendar._id} name={calendar._id} type="checkbox"
          checked={activeCalendars[calendar._id] || false}
          onChange={this._toggleCalendar(calendar._id)} />
        {calendar.name}

    ));
    controls.unshift(

        <input id="all-calendars" name="all-calendars" type="checkbox"
          checked={Object.keys(activeCalendars).length === 0}
          onChange={() => this.setState({ activeCalendars: {} },
            this._throttledLoad)} />
        All

    );
    return (

        {controls}

    );
  }

  render () {
    const { params: { id } } = this.props;
    const { calendar, filterActive, searchText, loading } = this.state;
    const date = moment(calendar.date);
    const daysOfWeek = this._renderDaysOfWeek();
    const weeks = loading ? undefined : this._renderWeeks();
    const loadingIndicator = loading ? <Loading /> : undefined;

    let filter;
    if (filterActive) {
      filter = this._renderFilter();
    }

    let actions = [];
    if (id) {
      actions.push(
        <Link key="add"
          to={`/events/add?calendarId=${encodeURIComponent(calendar._id)}`}
          className="a-header">Add</Link>
      );
      actions.push(
        <Link key="edit" to={`/calendars/${calendar._id}/edit`}
          className="a-header">Edit</Link>
      );
    } else {
      actions.push(

            <Button label="Calendars" className="button-header"
              onClick={() => this.setState({
                filterActive: ! this.state.filterActive})}/>
            {filter}

      );
    }

    let months = [];
    let monthDate = moment(date).startOf('year');
    while (monthDate.year() === date.year()) {
      months.push(
        <option key={monthDate.month()}>{monthDate.format('MMMM')}</option>
      );
      monthDate.add(1, 'month');
    }

    let years = [];
    let now = moment();
    let yearDate = moment().subtract(3, 'years');
    while (yearDate.year() <= (now.year() + 2)) {
      years.push(
        <option key={yearDate.year()}>{yearDate.format('YYYY')}</option>
      );
      yearDate.add(1, 'year');
    }

    let today;
    if (! date.isSame(now, 'date')) {
      today = Today;
    }

    let pageContext;
    if (id && calendar) {
      pageContext = (
        <PageContext
          filter={{ 'sections.calendarId': calendar._id }} />
      );
    }

    return (

        <PageHeader title={calendar.name || 'Calendar'}
          homer={id ? false : true}
          back={id ? true : false}
          searchText={searchText} onSearch={this._onSearch}
          actions={actions} />

            <button type="button" className="button-icon"
              onClick={this._changeDate(moment(calendar.previous))}>
              <LeftIcon />
            </button>

              <select value={moment(calendar.date).format('MMMM')}
                onChange={this._onChangeMonth}>
                {months}
              </select>
              <select value={moment(calendar.date).format('YYYY')}
                onChange={this._onChangeYear}>
                {years}
              </select>
              {today}

            <button type="button" className="button-icon"
              onClick={this._changeDate(moment(calendar.next))}>
              <RightIcon />
            </button>

            {daysOfWeek}

          {weeks}
          {loadingIndicator}

        {pageContext}

    );
  }
};

Calendar.propTypes = {
  location: PropTypes.shape({
    query: PropTypes.object
  }),
  params: PropTypes.shape({
    id: PropTypes.string
  })
};

Calendar.contextTypes = {
  router: PropTypes.any
};
<|endoftext|>


<commit_before>'use strict';

function shiftParamsFactory (
    coordsHelper,
    editActionsFactory,
    logger,
    parser,
    selectionCoordsHelper,
    selectionExpressionHelper,
    selectionHelper,
    utilities,
    vsCodeFactory
) {

    return function (_, callback) {

        function first(values) {
            return values[0];
        }

        function last(values) {
            return values[values.length - 1];
        }

        function getOuterAstCoords(expressionArray) {
            return {
                start: first(expressionArray).loc.start,
                end: last(expressionArray).loc.end
            }
        }
        function buildParamValues(functionExpression, sourceLines) {
            return functionExpression.params.map(function(paramNode) {
                const paramEditorCoords = coordsHelper.coordsFromAstToEditor(paramNode.loc);
                return selectionHelper.getSelection(sourceLines, paramEditorCoords)[0];
            });
        }

        function buildShiftCountRange(paramCount) {
            let shiftRange = [1];

            for(let i = 2; i < paramCount; i++) {
                shiftRange.push(i);
            }

            return shiftRange;
        }

        function buildShiftCountValues(shiftCountRange) {
            return shiftCountRange.map(function(count) {
                return count + (count === 1 ? ' place' : ' places');
            });
        }

        function shiftLeft(params, count) {
            const trailingParams = params.slice(0, count);
            const leadingParams = params.slice(count);

            return leadingParams.concat(trailingParams).join(', ');
        }

        return function () {
            const activeEditor = vsCodeFactory.get().window.activeTextEditor;
            const selectionEditorCoords = selectionCoordsHelper.getSelectionEditorCoords(activeEditor);
            const selectionAstCoords = coordsHelper.coordsFromEditorToAst(selectionEditorCoords);
            const sourceLines = utilities.getDocumentLines(activeEditor);

            const ast = parser.parseSourceLines(sourceLines);
            const nearestFunctionExpression = selectionExpressionHelper.getNearestFunctionExpression(selectionAstCoords, ast);

            if(nearestFunctionExpression === null) {
                logger.info('Cannot locate function for shift params action.');
            } else {
                const editActions = editActionsFactory(activeEditor);
                const paramValues = buildParamValues(nearestFunctionExpression, sourceLines);
                const shiftDirections = ['left', 'right'];
                const shiftDirectionOptions = {
                    message: 'Direction to shift params:'
                };

                logger.quickPick(shiftDirections, shiftDirectionOptions, function (direction) {
                    const shiftCountRange = buildShiftCountRange(paramValues.length);
                    const shiftCountOptions = {
                        message: 'Number of places to shift function params:'
                    };

                    logger.quickPick(buildShiftCountValues(shiftCountRange), shiftCountOptions, function (shiftSelection) {
                        const shiftCount = parseInt(shiftSelection.split(' ')[0]);
                        const directedShiftCount = direction === 'left' ? shiftCount : paramValues.length - shiftCount;

                        const shiftedParams = shiftLeft(paramValues, directedShiftCount);
                        const paramsAstCoords = getOuterAstCoords(nearestFunctionExpression.params);
                        const paramsEditorCoords = coordsHelper.coordsFromAstToEditor(paramsAstCoords);

                        editActions.applySetEdit(shiftedParams, paramsEditorCoords);
                    });
                });
            }
        }
    }
}

module.exports = shiftParamsFactory;<commit_msg>I forgot to call the callback after shift params was done<commit_after>'use strict';

function shiftParamsFactory (
    coordsHelper,
    editActionsFactory,
    logger,
    parser,
    selectionCoordsHelper,
    selectionExpressionHelper,
    selectionHelper,
    utilities,
    vsCodeFactory
) {

    return function (_, callback) {

        function first(values) {
            return values[0];
        }

        function last(values) {
            return values[values.length - 1];
        }

        function getOuterAstCoords(expressionArray) {
            return {
                start: first(expressionArray).loc.start,
                end: last(expressionArray).loc.end
            }
        }
        function buildParamValues(functionExpression, sourceLines) {
            return functionExpression.params.map(function(paramNode) {
                const paramEditorCoords = coordsHelper.coordsFromAstToEditor(paramNode.loc);
                return selectionHelper.getSelection(sourceLines, paramEditorCoords)[0];
            });
        }

        function buildShiftCountRange(paramCount) {
            let shiftRange = [1];

            for(let i = 2; i < paramCount; i++) {
                shiftRange.push(i);
            }

            return shiftRange;
        }

        function buildShiftCountValues(shiftCountRange) {
            return shiftCountRange.map(function(count) {
                return count + (count === 1 ? ' place' : ' places');
            });
        }

        function shiftLeft(params, count) {
            const trailingParams = params.slice(0, count);
            const leadingParams = params.slice(count);

            return leadingParams.concat(trailingParams).join(', ');
        }

        return function () {
            const activeEditor = vsCodeFactory.get().window.activeTextEditor;
            const selectionEditorCoords = selectionCoordsHelper.getSelectionEditorCoords(activeEditor);
            const selectionAstCoords = coordsHelper.coordsFromEditorToAst(selectionEditorCoords);
            const sourceLines = utilities.getDocumentLines(activeEditor);

            const ast = parser.parseSourceLines(sourceLines);
            const nearestFunctionExpression = selectionExpressionHelper.getNearestFunctionExpression(selectionAstCoords, ast);

            if(nearestFunctionExpression === null) {
                logger.info('Cannot locate function for shift params action.');
            } else {
                const editActions = editActionsFactory(activeEditor);
                const paramValues = buildParamValues(nearestFunctionExpression, sourceLines);
                const shiftDirections = ['left', 'right'];
                const shiftDirectionOptions = {
                    message: 'Direction to shift params:'
                };

                logger.quickPick(shiftDirections, shiftDirectionOptions, function (direction) {
                    const shiftCountRange = buildShiftCountRange(paramValues.length);
                    const shiftCountOptions = {
                        message: 'Number of places to shift function params:'
                    };

                    logger.quickPick(buildShiftCountValues(shiftCountRange), shiftCountOptions, function (shiftSelection) {
                        const shiftCount = parseInt(shiftSelection.split(' ')[0]);
                        const directedShiftCount = direction === 'left' ? shiftCount : paramValues.length - shiftCount;

                        const shiftedParams = shiftLeft(paramValues, directedShiftCount);
                        const paramsAstCoords = getOuterAstCoords(nearestFunctionExpression.params);
                        const paramsEditorCoords = coordsHelper.coordsFromAstToEditor(paramsAstCoords);

                        editActions.applySetEdit(shiftedParams, paramsEditorCoords);
                        callback();
                    });
                });
            }
        }
    }
}

module.exports = shiftParamsFactory;<|endoftext|>


<commit_before>'use strict';

const request = require('request-promise');

module.exports = function(app){
    function setHeaders(res, origin){
        res.header(origin.headers);
        res.header('Access-Control-Allow-Origin', '*');
        res.header('Access-Control-Allow-Credentials', false);
        res.header('Access-Control-Allow-Headers', 'Content-Type');
        res.header('X-Proxied-By', 'cors-container');
    }

    app.get('/*', (req, res) => {
        console.info(req.protocol + '://' + req.get('host') + req.originalUrl);

        request({
            uri: req.params[0],
            resolveWithFullResponse: true,
            headers: {
                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36'
            }
        })
        .then(originResponse => {
            setHeaders(res, originResponse);
            res.send(originResponse.body);
        })
        .catch(originResponse => {
            setHeaders(res, originResponse);
            res.status(originResponse.statusCode);
            return res.send('error');
        });
    });
};
<commit_msg>Handle errors better<commit_after>'use strict';

const request = require('request-promise');

module.exports = function(app){
    function setHeaders(res, origin){
        res.header(origin.headers);
        res.header('Access-Control-Allow-Origin', '*');
        res.header('Access-Control-Allow-Credentials', false);
        res.header('Access-Control-Allow-Headers', 'Content-Type');
        res.header('X-Proxied-By', 'cors-container');
    }

    app.get('/*', (req, res) => {
        console.info(req.protocol + '://' + req.get('host') + req.originalUrl);

        request({
            uri: req.params[0],
            resolveWithFullResponse: true,
            headers: {
                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36'
            }
        })
        .then(originResponse => {
            setHeaders(res, originResponse);
            res.send(originResponse.body);
        })
        .catch(originResponse => {
            setHeaders(res, originResponse);
            res.status(originResponse.statusCode || 500);
            return res.send(originResponse.message);
        });
    });
};
<|endoftext|>


<commit_before>import VueRouter from 'vue-router';
import { RouterNames } from './constants';
import TreeView from './views/TreeView';
import StagingTreePage from './pages/StagingTreePage';
import store from './store';
import AddPreviousStepsPage from './pages/AddPreviousStepsPage';
import AddNextStepsPage from './pages/AddNextStepsPage';
import TrashModal from './views/trash/TrashModal';
import SearchOrBrowseWindow from './views/ImportFromChannels/SearchOrBrowseWindow';
import ReviewSelectionsPage from './views/ImportFromChannels/ReviewSelectionsPage';
import EditModal from './components/edit/EditModal';
import { RouterNames as ChannelRouterNames } from 'frontend/channelList/constants';
import Sandbox from 'shared/views/Sandbox';
import ChannelModal from 'shared/views/channel/ChannelModal';
import ChannelDetailsModal from 'shared/views/channel/ChannelDetailsModal';

const router = new VueRouter({
  routes: [
    {
      name: RouterNames.SANDBOX,
      path: '/sandbox/:nodeId',
      props: true,
      component: Sandbox,
      beforeEnter: (to, from, next) => {
        const channelPromise = store.dispatch('currentChannel/loadChannel');
        const treePromise = store.dispatch(
          'contentNode/loadTree',
          store.state.currentChannel.currentChannelId
        );
        const nodePromise = store.dispatch('contentNode/loadContentNode', to.params.nodeId);
        // api call to get ancestors if nodeId is a child descendant???
        return Promise.all([channelPromise, treePromise, nodePromise])
          .then(() => next())
          .catch(() => {});
      },
    },
    {
      name: RouterNames.TREE_ROOT_VIEW,
      path: '/',
      beforeEnter: (to, from, next) => {
        return store.dispatch('currentChannel/loadChannel').then(channel => {
          const nodeId = channel.root_id;
          return next({
            name: RouterNames.TREE_VIEW,
            params: {
              nodeId,
            },
          });
        });
      },
    },
    {
      name: RouterNames.IMPORT_FROM_CHANNELS_BROWSE,
      path: '/import/:destNodeId/browse/:channelId?/:nodeId?',
      component: SearchOrBrowseWindow,
      props: true,
    },
    {
      name: RouterNames.IMPORT_FROM_CHANNELS_SEARCH,
      path: '/import/:destNodeId/search/:searchTerm',
      component: SearchOrBrowseWindow,
      props: true,
    },
    {
      name: RouterNames.IMPORT_FROM_CHANNELS_REVIEW,
      path: '/import/:destNodeId/review',
      component: ReviewSelectionsPage,
      props: true,
    },
    {
      name: RouterNames.STAGING_TREE_VIEW,
      path: '/staging/:nodeId/:detailNodeId?',
      props: true,
      component: StagingTreePage,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel', { staging: true })
          .then(channel => {
            if (channel.staging_root_id) {
              return store.dispatch('contentNode/loadTree', { tree_id: channel.staging_root_id });
            }
          })
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.ADD_PREVIOUS_STEPS,
      path: '/previous-steps/:nodeId',
      props: true,
      component: AddPreviousStepsPage,
      beforeEnter: (to, from, next) => {
        const { currentChannelId } = store.state.currentChannel;
        const { nodeId } = to.params;
        const promises = [
          store.dispatch('channel/loadChannel', currentChannelId),
          store.dispatch('contentNode/loadRelatedResources', nodeId),
        ];

        return Promise.all(promises)
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.ADD_NEXT_STEPS,
      path: '/next-steps/:nodeId',
      props: true,
      component: AddNextStepsPage,
      beforeEnter: (to, from, next) => {
        const { currentChannelId } = store.state.currentChannel;
        const { nodeId } = to.params;
        const promises = [
          store.dispatch('channel/loadChannel', currentChannelId),
          store.dispatch('contentNode/loadRelatedResources', nodeId),
        ];

        return Promise.all(promises)
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.TRASH,
      path: '/:nodeId/:detailNodeId?/trash',
      component: TrashModal,
      props: true,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel')
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: ChannelRouterNames.CHANNEL_DETAILS,
      path: '/:nodeId/:detailNodeId?/channel/:channelId/details',
      component: ChannelDetailsModal,
      props: true,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel')
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: ChannelRouterNames.CHANNEL_EDIT,
      path: '/:nodeId/:detailNodeId?/channel/:channelId/edit',
      component: ChannelModal,
      props: true,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel')
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.CONTENTNODE_DETAILS,
      path: '/:nodeId/:detailNodeId?/details/:detailNodeIds/:tab?',
      props: true,
      component: EditModal,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel')
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.ADD_TOPICS,
      path: '/:nodeId/:detailNodeId?/topics/:detailNodeIds/:tab?',
      props: true,
      component: EditModal,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel')
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.ADD_EXERCISE,
      path: '/:nodeId/:detailNodeId?/exercise/:detailNodeIds/:tab?',
      props: true,
      component: EditModal,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel')
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.UPLOAD_FILES,
      path: '/:nodeId/:detailNodeId?/upload/:detailNodeIds?/:tab?',
      props: true,
      component: EditModal,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel')
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.TREE_VIEW,
      path: '/:nodeId/:detailNodeId?',
      props: true,
      component: TreeView,
      beforeEnter: (to, from, next) => {
        const { currentChannelId } = store.state.currentChannel;

        return store
          .dispatch('currentChannel/loadChannel')
          .then(() => {
            return store.dispatch('contentNode/loadChannelTree', currentChannelId);
          })
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
  ],
});

export default router;
<commit_msg>add replace: true to next() in the root tree view nav guard<commit_after>import VueRouter from 'vue-router';
import { RouterNames } from './constants';
import TreeView from './views/TreeView';
import StagingTreePage from './pages/StagingTreePage';
import store from './store';
import AddPreviousStepsPage from './pages/AddPreviousStepsPage';
import AddNextStepsPage from './pages/AddNextStepsPage';
import TrashModal from './views/trash/TrashModal';
import SearchOrBrowseWindow from './views/ImportFromChannels/SearchOrBrowseWindow';
import ReviewSelectionsPage from './views/ImportFromChannels/ReviewSelectionsPage';
import EditModal from './components/edit/EditModal';
import { RouterNames as ChannelRouterNames } from 'frontend/channelList/constants';
import Sandbox from 'shared/views/Sandbox';
import ChannelModal from 'shared/views/channel/ChannelModal';
import ChannelDetailsModal from 'shared/views/channel/ChannelDetailsModal';

const router = new VueRouter({
  routes: [
    {
      name: RouterNames.SANDBOX,
      path: '/sandbox/:nodeId',
      props: true,
      component: Sandbox,
      beforeEnter: (to, from, next) => {
        const channelPromise = store.dispatch('currentChannel/loadChannel');
        const treePromise = store.dispatch(
          'contentNode/loadTree',
          store.state.currentChannel.currentChannelId
        );
        const nodePromise = store.dispatch('contentNode/loadContentNode', to.params.nodeId);
        // api call to get ancestors if nodeId is a child descendant???
        return Promise.all([channelPromise, treePromise, nodePromise])
          .then(() => next())
          .catch(() => {});
      },
    },
    {
      name: RouterNames.TREE_ROOT_VIEW,
      path: '/',
      beforeEnter: (to, from, next) => {
        return store.dispatch('currentChannel/loadChannel').then(channel => {
          const nodeId = channel.root_id;
          return next({
            name: RouterNames.TREE_VIEW,
            params: {
              nodeId,
            },
            replace: true,
          });
        });
      },
    },
    {
      name: RouterNames.IMPORT_FROM_CHANNELS_BROWSE,
      path: '/import/:destNodeId/browse/:channelId?/:nodeId?',
      component: SearchOrBrowseWindow,
      props: true,
    },
    {
      name: RouterNames.IMPORT_FROM_CHANNELS_SEARCH,
      path: '/import/:destNodeId/search/:searchTerm',
      component: SearchOrBrowseWindow,
      props: true,
    },
    {
      name: RouterNames.IMPORT_FROM_CHANNELS_REVIEW,
      path: '/import/:destNodeId/review',
      component: ReviewSelectionsPage,
      props: true,
    },
    {
      name: RouterNames.STAGING_TREE_VIEW,
      path: '/staging/:nodeId/:detailNodeId?',
      props: true,
      component: StagingTreePage,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel', { staging: true })
          .then(channel => {
            if (channel.staging_root_id) {
              return store.dispatch('contentNode/loadTree', { tree_id: channel.staging_root_id });
            }
          })
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.ADD_PREVIOUS_STEPS,
      path: '/previous-steps/:nodeId',
      props: true,
      component: AddPreviousStepsPage,
      beforeEnter: (to, from, next) => {
        const { currentChannelId } = store.state.currentChannel;
        const { nodeId } = to.params;
        const promises = [
          store.dispatch('channel/loadChannel', currentChannelId),
          store.dispatch('contentNode/loadRelatedResources', nodeId),
        ];

        return Promise.all(promises)
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.ADD_NEXT_STEPS,
      path: '/next-steps/:nodeId',
      props: true,
      component: AddNextStepsPage,
      beforeEnter: (to, from, next) => {
        const { currentChannelId } = store.state.currentChannel;
        const { nodeId } = to.params;
        const promises = [
          store.dispatch('channel/loadChannel', currentChannelId),
          store.dispatch('contentNode/loadRelatedResources', nodeId),
        ];

        return Promise.all(promises)
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.TRASH,
      path: '/:nodeId/:detailNodeId?/trash',
      component: TrashModal,
      props: true,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel')
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: ChannelRouterNames.CHANNEL_DETAILS,
      path: '/:nodeId/:detailNodeId?/channel/:channelId/details',
      component: ChannelDetailsModal,
      props: true,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel')
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: ChannelRouterNames.CHANNEL_EDIT,
      path: '/:nodeId/:detailNodeId?/channel/:channelId/edit',
      component: ChannelModal,
      props: true,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel')
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.CONTENTNODE_DETAILS,
      path: '/:nodeId/:detailNodeId?/details/:detailNodeIds/:tab?',
      props: true,
      component: EditModal,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel')
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.ADD_TOPICS,
      path: '/:nodeId/:detailNodeId?/topics/:detailNodeIds/:tab?',
      props: true,
      component: EditModal,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel')
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.ADD_EXERCISE,
      path: '/:nodeId/:detailNodeId?/exercise/:detailNodeIds/:tab?',
      props: true,
      component: EditModal,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel')
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.UPLOAD_FILES,
      path: '/:nodeId/:detailNodeId?/upload/:detailNodeIds?/:tab?',
      props: true,
      component: EditModal,
      beforeEnter: (to, from, next) => {
        return store
          .dispatch('currentChannel/loadChannel')
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
    {
      name: RouterNames.TREE_VIEW,
      path: '/:nodeId/:detailNodeId?',
      props: true,
      component: TreeView,
      beforeEnter: (to, from, next) => {
        const { currentChannelId } = store.state.currentChannel;

        return store
          .dispatch('currentChannel/loadChannel')
          .then(() => {
            return store.dispatch('contentNode/loadChannelTree', currentChannelId);
          })
          .catch(error => {
            throw new Error(error);
          })
          .then(() => next());
      },
    },
  ],
});

export default router;
<|endoftext|>


<commit_before>'use strict';
(function (angular) {
    angular
        .module('couponPluginContent')
        .controller('ContentItemCtrl', ['$scope', '$routeParams', '$timeout', 'DEFAULT_DATA', 'DataStore', 'TAG_NAMES', 'Location', 'Utils', 'Modals', 'RankOfLastFilter', 'Buildfire',
            function ($scope, $routeParams, $timeout, DEFAULT_DATA, DataStore, TAG_NAMES, Location, Utils, Modals, RankOfLastFilter, Buildfire) {
                var ContentItem = this;
                var tmrDelayForItem = null
                    , isNewItemInserted = false
                    , updating = false;
                ContentItem.filters=[];

                /**
                 * This updateMasterItem will update the ContentMedia.masterItem with passed item
                 * @param item
                 */
                function updateMasterItem(item) {
                    ContentItem.masterItem = angular.copy(item);
                }

                /**
                 * This resetItem will reset the ContentMedia.item with ContentMedia.masterItem
                 */
                function resetItem() {
                    ContentItem.item = angular.copy(ContentItem.masterItem);
                }

                /**
                 * isUnChanged to check whether there is change in controller media item or not
                 * @param item
                 * @returns {*|boolean}
                 */

                ContentItem.selection = [];

                ContentItem.toggleCategoriesSelection = function toggleCategoriesSelection(category) {
                    var idx = ContentItem.selection.indexOf(category);
                    // is currently selected
                    if (idx > -1) {
                        ContentItem.selection.splice(idx, 1);
                    }

                    // is newly selected
                    else {
                        ContentItem.selection.push(category);
                    }
                    ContentItem.item.data.SelectedCategories = ContentItem.selection;
                    //insertAndUpdate(ContentItem.item)
                }
                function isUnChanged(item) {
                    return angular.equals(item, ContentItem.masterItem);
                }

                function isValidItem(item) {
                    if(item){
                        return item.title;
                    }
                    else{
                        return false;
                    }
                }

                function isValidFilter(item) {
                    if(item){
                        return item.title;
                    }
                    else{
                        return false;
                    }
                }

                function insertAndUpdate(_item) {
                    updating = true;
                    if (_item.id) {
                        DataStore.update(_item.id, _item.data, TAG_NAMES.COUPON_ITEMS).then(function (data) {
                            console.log('Item updated successfully-----', data);
                            updateMasterItem(data);
                            updating = false;
                        }, function (err) {
                            console.error('Error: while updating item--:', err);
                            resetItem();
                            updating = false;
                        });
                    }
                    else if (!isNewItemInserted) {
                        isNewItemInserted = true;
                        _item.data.dateCreated = +new Date();
                        DataStore.insert(_item.data, TAG_NAMES.COUPON_ITEMS).then(function (data) {
                            updating = false;
                            if (data && data.id) {
                                ContentItem.item.data.deepLinkUrl = buildfire.deeplink.createLink({id: data.id});
                                ContentItem.item.id = data.id;
                                updateMasterItem(ContentItem.item);
                            }
                            else {
                                isNewItemInserted = false;
                            }
                        }, function (err) {
                            console.error('Error: while inserting item--:', err);
                            resetItem();
                            updating = false;
                            isNewItemInserted = false;
                        });
                    }
                }

                /**
                 * updateItemsWithDelay called when ever there is some change in current item
                 * @param _item
                 */
                function updateItemsWithDelay(_item) {
                    if (updating)
                        return;
                    if (tmrDelayForItem) {
                        $timeout.cancel(tmrDelayForItem);
                    }
                    if(ContentItem.item) {
                        ContentItem.isItemValid = isValidItem(ContentItem.item.data);
                        if (_item && !isUnChanged(_item) && ContentItem.isItemValid) {
                            tmrDelayForItem = $timeout(function () {
                                insertAndUpdate(_item);
                            }, 300);
                        }
                    }
                }

                function init() {
                    //if ($routeParams.id) {
                    //}
                    //else {
                        var searchOptions={
                            "filter":{"$json.title": {"$regex": '/*'}},
                            "sort": {"title": 1},
                            "skip":"0",
                            "limit":"50"
                        };

                        Buildfire.datastore.search(searchOptions, TAG_NAMES.COUPON_CATEGORIES, function (err, result) {
                            if(!ContentItem.item)
                            ContentItem.item = angular.copy(DEFAULT_DATA.ITEM);
                            if (err) {
                                Buildfire.spinner.hide();
                                return console.error('-----------err in getting list-------------', err);
                            }
                            var tmpArray=[];
                            var lastIndex=result.length;
                            result.forEach(function(res,index){
                                tmpArray.push({'title' : res.data.title,
                                    id:res.data.id});
                            });

                            ContentItem.item.data.Categories = tmpArray;
                            updateMasterItem(ContentItem.item);
                            Buildfire.spinner.hide();
                            $scope.$digest();
                        });
                    }
                //}

                ContentItem.getItemData = function(itemId){
                    var success = function(result){
                          console.log("------------->>>>", result, itemId);
                          ContentItem.item = result;
                          ContentItem.selection = ContentItem.item.data.SelectedCategories;
                      },
                      error = function(err){
                          console.log("There is error in fetching data", err);
                      }
                    DataStore.getById(itemId, TAG_NAMES.COUPON_ITEMS).then(success, error);
                 }

                if ($routeParams.id) {
                    ContentItem.getItemData($routeParams.id)
                }
                else{
                    init();
                }

                ContentItem.addListImage = function () {
                    var options = {showIcons: false, multiSelection: false},
                        listImgCB = function (error, result) {
                            if (error) {
                                console.error('Error:', error);
                            } else {
                                ContentItem.item.data.listImage = result && result.selectedFiles && result.selectedFiles[0] || null;
                                if (!$scope.$$phase)$scope.$digest();
                            }
                        };
                    buildfire.imageLib.showDialog(options, listImgCB);
                };
                ContentItem.removeListImage = function () {
                    ContentItem.item.data.listImage = null;
                };

                /**
                 * done will close the single item view
                 */
                ContentItem.done = function () {
                    Location.goToHome();
                };
                ContentItem.setLocation = function (data) {
                    console.log('setLocation-------------------method called-----------', data);
                    ContentItem.item.data.location = {
                        coordinates: {
                            lng: data.coordinates[0],
                            lat: data.coordinates[1]
                        },
                        addressTitle: data.location
                    };
                    $timeout(function () {
                        ContentItem.currentAddress = data.location;
                        ContentItem.currentCoordinates = data.coordinates;
                    }, 0);
                };

                ContentItem.setDraggedLocation = function (data) {
                    ContentItem.item.data.address = {
                        lng: data.coordinates[0],
                        lat: data.coordinates[1],
                        aName: data.location
                    };
                    ContentItem.currentAddress = data.location;
                    ContentItem.currentCoordinates = data.coordinates;
                    $scope.$digest();
                };
                ContentItem.setCoordinates = function () {
                    var latlng = '';
                    console.log('ng-enter---------------------called------------------', ContentItem.currentAddress);
                    function successCallback(resp) {
                        console.error('Successfully validated coordinates-----------', resp);
                        if (resp) {
                            ContentItem.item.data.address = {
                                lng: ContentItem.currentAddress.split(",")[1].trim(),
                                lat: ContentItem.currentAddress.split(",")[0].trim(),
                                aName: ContentItem.currentAddress
                            };
                            ContentItem.currentCoordinates = [ContentItem.currentAddress.split(",")[1].trim(), ContentItem.currentAddress.split(",")[0].trim()];
                        } else {
                            //errorCallback();
                        }
                    }

                    function errorCallback(err) {
                        console.error('Error while validating coordinates------------', err);
                        ContentItem.validCoordinatesFailure = true;
                        $timeout(function () {
                            ContentItem.validCoordinatesFailure = false;
                        }, 5000);
                    }

                    if (ContentItem.currentAddress) {
                        latlng = ContentItem.currentAddress.split(',')[1] + "," + ContentItem.currentAddress.split(',')[0]
                    }

                    Utils.validLongLats(latlng).then(successCallback, errorCallback);
                };
                ContentItem.clearData = function () {
                    if (!ContentItem.currentAddress) {
                        ContentItem.item.data.address = {
                            lng: '',
                            lat: '',
                            aName: ''
                        };
                        ContentItem.currentCoordinates = null;
                    }
                };

                ContentItem.validCopyAddressFailure = false;
                ContentItem.locationAutocompletePaste = function () {
                    function error() {
                        console.error('ERROOR emethpdd called');
                        ContentItem.validCopyAddressFailure = true;
                        $timeout(function () {
                            ContentItem.validCopyAddressFailure = false;
                        }, 5000);

                    }

                    $timeout(function () {
                        console.log('val>>>', $("#googleMapAutocomplete").val());
                        console.log('.pac-container .pac-item', $(".pac-container .pac-item").length);
                        if ($(".pac-container .pac-item").length) {
                            var firstResult = $(".pac-container .pac-item:first").find('.pac-matched').map(function () {
                                return $(this).text();
                            }).get().join(); // + ', ' + $(".pac-container .pac-item:first").find('span:last').text();
                            console.log('firstResult', firstResult);
                            var geocoder = new google.maps.Geocoder();
                            geocoder.geocode({"address": firstResult}, function (results, status) {
                                if (status == google.maps.GeocoderStatus.OK) {
                                    var lat = results[0].geometry.location.lat(),
                                        lng = results[0].geometry.location.lng();
                                    ContentItem.setLocation({location: firstResult, coordinates: [lng, lat]});
                                    $("#googleMapAutocomplete").blur();
                                }
                                else {
                                    console.error('' +
                                        'Error else parts of google');
                                    error();
                                }
                            });
                        }
                        else if (ContentItem.currentAddress && ContentItem.currentAddress.split(',').length) {
                            console.log('Location found---------------------', ContentItem.currentAddress.split(',').length, ContentItem.currentAddress.split(','));
                            ContentItem.setCoordinates();
                            /*var geocoder = new google.maps.Geocoder();
                             geocoder.geocode({
                             "latLng": {
                             "lat": parseInt(ContentItem.currentAddress.split(',')[0]),
                             "lng": parseInt(ContentItem.currentAddress.split(',')[1])
                             }
                             }, function (results, status) {
                             console.log('Got Address based on coordinates--------------------', results, status);
                             if (status == google.maps.GeocoderStatus.OK) {
                             var lat = results[0].geometry.location.lat(),
                             lng = results[0].geometry.location.lng();
                             ContentItem.setLocation({location: ContentItem.currentAddress, coordinates: [lng, lat]});
                             $("#googleMapAutocomplete").blur();
                             }
                             else {
                             console.error('' +
                             'Error else parts of google');
                             error();
                             }
                             });*/
                        }
                        else {
                            error();
                        }
                    }, 1000);

                };

                ContentItem.addFilter = function () {
                    Modals.addFilterModal({
                        title: '',
                        isEdit: false
                    }).then(function (response) {
                        console.log('Response of a popup----------------------------', response);
                        if (!(response.title === null || response.title.match(/^ *$/) !== null)) {

                            //if index is there it means filter update operation is performed
                            ContentItem.filter = {
                                title: response.title,
                                rank: RankOfLastFilter.getRank() + 10
                            };
                            //ContentItem.data.content.rankOfLastFilter = RankOfLastFilter.getRank() + 1;
                           // RankOfLastFilter.setRank(ContentItem.data.content.rankOfLastFilter);
                            ContentItem.filters.unshift(ContentItem.filter);
                            Buildfire.datastore.insert(ContentItem.filter, TAG_NAMES.COUPON_CATEGORIES, false, function (err, data) {
                                console.log("Saved", data.id);
                                ContentItem.isUpdating = false;
                                ContentItem.filter.id = data.id;
                                if (err) {
                                    ContentItem.isNewItemInserted = false;
                                    return console.error('There was a problem saving your data');
                                }
                                $scope.$digest();
                            });
                        }
                    }, function (err) {

                    });
                };

                //option for wysiwyg
                ContentItem.bodyWYSIWYGOptions = {
                    plugins: 'advlist autolink link image lists charmap print preview',
                    skin: 'lightgray',
                    trusted: true,
                    theme: 'modern',
                    plugin_preview_width: "500",
                    plugin_preview_height: "500"
                };

                var updateFilterWithDelay = function (item) {
                    ContentItem.isUpdating = false;
                    ContentItem.isItemValid = isValidFilter(ContentItem.filter);
                    if (!ContentItem.isUpdating && ContentItem.isItemValid) {
                        setTimeout(function () {
                            if (item.id) {
                                ContentItem.updateItemData();
                                $scope.$digest();
                            } /*else if (!ContentHome.isNewItemInserted) {
                             ContentHome.addNewItem();
                             }*/
                        }, 300);
                    }
                };

            ContentItem.updateItemData = function () {
                    Buildfire.datastore.update(ContentItem.filter.id, ContentItem.filter, TAG_NAMES.COUPON_CATEGORIES, function (err) {
                        ContentItem.isUpdating = false;
                        init();
                        if (err)
                            return console.error('There was a problem saving your data');
                    })
                };

                $scope.$watch(function () {
                    return ContentItem.item;
                }, updateItemsWithDelay, true);

                /*
                 * watch for changes in filters and trigger the saveDataWithDelay function on change
                 * */
                $scope.$watch(function () {
                    return ContentItem.filter;
                }, updateFilterWithDelay, true);

            }]);
})(window.angular);<commit_msg>Added sendMessageToWidget in itemController in content section.<commit_after>'use strict';
(function (buildfire, angular) {
    angular
        .module('couponPluginContent')
        .controller('ContentItemCtrl', ['$scope', '$routeParams', '$timeout', 'DEFAULT_DATA', 'DataStore', 'TAG_NAMES', 'Location', 'Utils', 'Modals', 'RankOfLastFilter', 'Buildfire',
            function ($scope, $routeParams, $timeout, DEFAULT_DATA, DataStore, TAG_NAMES, Location, Utils, Modals, RankOfLastFilter, Buildfire) {
                var ContentItem = this;
                var tmrDelayForItem = null
                    , isNewItemInserted = false
                    , updating = false;
                ContentItem.filters=[];

                /**
                 * This updateMasterItem will update the ContentMedia.masterItem with passed item
                 * @param item
                 */
                function updateMasterItem(item) {
                    ContentItem.masterItem = angular.copy(item);
                }

                /**
                 * This resetItem will reset the ContentMedia.item with ContentMedia.masterItem
                 */
                function resetItem() {
                    ContentItem.item = angular.copy(ContentItem.masterItem);
                }

                /**
                 * isUnChanged to check whether there is change in controller media item or not
                 * @param item
                 * @returns {*|boolean}
                 */

                ContentItem.selection = [];

                ContentItem.toggleCategoriesSelection = function toggleCategoriesSelection(category) {
                    var idx = ContentItem.selection.indexOf(category);
                    // is currently selected
                    if (idx > -1) {
                        ContentItem.selection.splice(idx, 1);
                    }

                    // is newly selected
                    else {
                        ContentItem.selection.push(category);
                    }
                    ContentItem.item.data.SelectedCategories = ContentItem.selection;
                    //insertAndUpdate(ContentItem.item)
                }
                function isUnChanged(item) {
                    return angular.equals(item, ContentItem.masterItem);
                }

                function isValidItem(item) {
                    if(item){
                        return item.title;
                    }
                    else{
                        return false;
                    }
                }

                function isValidFilter(item) {
                    if(item){
                        return item.title;
                    }
                    else{
                        return false;
                    }
                }

                function insertAndUpdate(_item) {
                    updating = true;
                    if (_item.id) {
                        DataStore.update(_item.id, _item.data, TAG_NAMES.COUPON_ITEMS).then(function (data) {
                            console.log('Item updated successfully-----', data);
                            updateMasterItem(data);
                            updating = false;
                        }, function (err) {
                            console.error('Error: while updating item--:', err);
                            resetItem();
                            updating = false;
                        });
                    }
                    else if (!isNewItemInserted) {
                        isNewItemInserted = true;
                        _item.data.dateCreated = +new Date();
                        DataStore.insert(_item.data, TAG_NAMES.COUPON_ITEMS).then(function (data) {
                            updating = false;
                            if (data && data.id) {
                                ContentItem.item.data.deepLinkUrl = buildfire.deeplink.createLink({id: data.id});
                                ContentItem.item.id = data.id;
                                updateMasterItem(ContentItem.item);
                                if(ContentItem.item.id)
                                buildfire.messaging.sendMessageToWidget({
                                    id:ContentItem.item.id,
                                    type:'AddNewItem'
                                });
                            }
                            else {
                                isNewItemInserted = false;
                            }
                        }, function (err) {
                            console.error('Error: while inserting item--:', err);
                            resetItem();
                            updating = false;
                            isNewItemInserted = false;
                        });
                    }
                }

                /**
                 * updateItemsWithDelay called when ever there is some change in current item
                 * @param _item
                 */
                function updateItemsWithDelay(_item) {
                    if (updating)
                        return;
                    if (tmrDelayForItem) {
                        $timeout.cancel(tmrDelayForItem);
                    }
                    if(ContentItem.item) {
                        ContentItem.isItemValid = isValidItem(ContentItem.item.data);
                        if (_item && !isUnChanged(_item) && ContentItem.isItemValid) {
                            tmrDelayForItem = $timeout(function () {
                                insertAndUpdate(_item);
                            }, 300);
                        }
                    }
                }

                function init() {
                    //if ($routeParams.id) {
                    //}
                    //else {
                        var searchOptions={
                            "filter":{"$json.title": {"$regex": '/*'}},
                            "sort": {"title": 1},
                            "skip":"0",
                            "limit":"50"
                        };

                        Buildfire.datastore.search(searchOptions, TAG_NAMES.COUPON_CATEGORIES, function (err, result) {
                            if(!ContentItem.item)
                            ContentItem.item = angular.copy(DEFAULT_DATA.ITEM);
                            if (err) {
                                Buildfire.spinner.hide();
                                return console.error('-----------err in getting list-------------', err);
                            }
                            var tmpArray=[];
                            var lastIndex=result.length;
                            result.forEach(function(res,index){
                                tmpArray.push({'title' : res.data.title,
                                    id:res.data.id});
                            });

                            ContentItem.item.data.Categories = tmpArray;
                            updateMasterItem(ContentItem.item);
                            Buildfire.spinner.hide();
                            $scope.$digest();
                        });
                    }
                //}

                ContentItem.getItemData = function(itemId){
                    var success = function(result){
                          console.log("------------->>>>", result, itemId);
                          ContentItem.item = result;
                          ContentItem.selection = ContentItem.item.data.SelectedCategories;
                      },
                      error = function(err){
                          console.log("There is error in fetching data", err);
                      }
                    DataStore.getById(itemId, TAG_NAMES.COUPON_ITEMS).then(success, error);
                 }

                 /*
                  Send message to widget that this page has been opened
                */

                if ($routeParams.id) {
                    ContentItem.getItemData($routeParams.id)
                    buildfire.messaging.sendMessageToWidget({
                        id: $routeParams.id,
                        type: 'OpenItem'
                    });
                }
                else{
                    init();
                }

                ContentItem.addListImage = function () {
                    var options = {showIcons: false, multiSelection: false},
                        listImgCB = function (error, result) {
                            if (error) {
                                console.error('Error:', error);
                            } else {
                                ContentItem.item.data.listImage = result && result.selectedFiles && result.selectedFiles[0] || null;
                                if (!$scope.$$phase)$scope.$digest();
                            }
                        };
                    buildfire.imageLib.showDialog(options, listImgCB);
                };
                ContentItem.removeListImage = function () {
                    ContentItem.item.data.listImage = null;
                };

                /**
                 * done will close the single item view
                 */
                ContentItem.done = function () {
                    Location.goToHome();
                };
                ContentItem.setLocation = function (data) {
                    console.log('setLocation-------------------method called-----------', data);
                    ContentItem.item.data.location = {
                        coordinates: {
                            lng: data.coordinates[0],
                            lat: data.coordinates[1]
                        },
                        addressTitle: data.location
                    };
                    $timeout(function () {
                        ContentItem.currentAddress = data.location;
                        ContentItem.currentCoordinates = data.coordinates;
                    }, 0);
                };

                ContentItem.setDraggedLocation = function (data) {
                    ContentItem.item.data.address = {
                        lng: data.coordinates[0],
                        lat: data.coordinates[1],
                        aName: data.location
                    };
                    ContentItem.currentAddress = data.location;
                    ContentItem.currentCoordinates = data.coordinates;
                    $scope.$digest();
                };
                ContentItem.setCoordinates = function () {
                    var latlng = '';
                    console.log('ng-enter---------------------called------------------', ContentItem.currentAddress);
                    function successCallback(resp) {
                        console.error('Successfully validated coordinates-----------', resp);
                        if (resp) {
                            ContentItem.item.data.address = {
                                lng: ContentItem.currentAddress.split(",")[1].trim(),
                                lat: ContentItem.currentAddress.split(",")[0].trim(),
                                aName: ContentItem.currentAddress
                            };
                            ContentItem.currentCoordinates = [ContentItem.currentAddress.split(",")[1].trim(), ContentItem.currentAddress.split(",")[0].trim()];
                        } else {
                            //errorCallback();
                        }
                    }

                    function errorCallback(err) {
                        console.error('Error while validating coordinates------------', err);
                        ContentItem.validCoordinatesFailure = true;
                        $timeout(function () {
                            ContentItem.validCoordinatesFailure = false;
                        }, 5000);
                    }

                    if (ContentItem.currentAddress) {
                        latlng = ContentItem.currentAddress.split(',')[1] + "," + ContentItem.currentAddress.split(',')[0]
                    }

                    Utils.validLongLats(latlng).then(successCallback, errorCallback);
                };
                ContentItem.clearData = function () {
                    if (!ContentItem.currentAddress) {
                        ContentItem.item.data.address = {
                            lng: '',
                            lat: '',
                            aName: ''
                        };
                        ContentItem.currentCoordinates = null;
                    }
                };

                ContentItem.validCopyAddressFailure = false;
                ContentItem.locationAutocompletePaste = function () {
                    function error() {
                        console.error('ERROOR emethpdd called');
                        ContentItem.validCopyAddressFailure = true;
                        $timeout(function () {
                            ContentItem.validCopyAddressFailure = false;
                        }, 5000);

                    }

                    $timeout(function () {
                        console.log('val>>>', $("#googleMapAutocomplete").val());
                        console.log('.pac-container .pac-item', $(".pac-container .pac-item").length);
                        if ($(".pac-container .pac-item").length) {
                            var firstResult = $(".pac-container .pac-item:first").find('.pac-matched').map(function () {
                                return $(this).text();
                            }).get().join(); // + ', ' + $(".pac-container .pac-item:first").find('span:last').text();
                            console.log('firstResult', firstResult);
                            var geocoder = new google.maps.Geocoder();
                            geocoder.geocode({"address": firstResult}, function (results, status) {
                                if (status == google.maps.GeocoderStatus.OK) {
                                    var lat = results[0].geometry.location.lat(),
                                        lng = results[0].geometry.location.lng();
                                    ContentItem.setLocation({location: firstResult, coordinates: [lng, lat]});
                                    $("#googleMapAutocomplete").blur();
                                }
                                else {
                                    console.error('' +
                                        'Error else parts of google');
                                    error();
                                }
                            });
                        }
                        else if (ContentItem.currentAddress && ContentItem.currentAddress.split(',').length) {
                            console.log('Location found---------------------', ContentItem.currentAddress.split(',').length, ContentItem.currentAddress.split(','));
                            ContentItem.setCoordinates();
                            /*var geocoder = new google.maps.Geocoder();
                             geocoder.geocode({
                             "latLng": {
                             "lat": parseInt(ContentItem.currentAddress.split(',')[0]),
                             "lng": parseInt(ContentItem.currentAddress.split(',')[1])
                             }
                             }, function (results, status) {
                             console.log('Got Address based on coordinates--------------------', results, status);
                             if (status == google.maps.GeocoderStatus.OK) {
                             var lat = results[0].geometry.location.lat(),
                             lng = results[0].geometry.location.lng();
                             ContentItem.setLocation({location: ContentItem.currentAddress, coordinates: [lng, lat]});
                             $("#googleMapAutocomplete").blur();
                             }
                             else {
                             console.error('' +
                             'Error else parts of google');
                             error();
                             }
                             });*/
                        }
                        else {
                            error();
                        }
                    }, 1000);

                };

                ContentItem.addFilter = function () {
                    Modals.addFilterModal({
                        title: '',
                        isEdit: false
                    }).then(function (response) {
                        console.log('Response of a popup----------------------------', response);
                        if (!(response.title === null || response.title.match(/^ *$/) !== null)) {

                            //if index is there it means filter update operation is performed
                            ContentItem.filter = {
                                title: response.title,
                                rank: RankOfLastFilter.getRank() + 10
                            };
                            //ContentItem.data.content.rankOfLastFilter = RankOfLastFilter.getRank() + 1;
                           // RankOfLastFilter.setRank(ContentItem.data.content.rankOfLastFilter);
                            ContentItem.filters.unshift(ContentItem.filter);
                            Buildfire.datastore.insert(ContentItem.filter, TAG_NAMES.COUPON_CATEGORIES, false, function (err, data) {
                                console.log("Saved", data.id);
                                ContentItem.isUpdating = false;
                                ContentItem.filter.id = data.id;
                                if (err) {
                                    ContentItem.isNewItemInserted = false;
                                    return console.error('There was a problem saving your data');
                                }
                                $scope.$digest();
                            });
                        }
                    }, function (err) {

                    });
                };

                //option for wysiwyg
                ContentItem.bodyWYSIWYGOptions = {
                    plugins: 'advlist autolink link image lists charmap print preview',
                    skin: 'lightgray',
                    trusted: true,
                    theme: 'modern',
                    plugin_preview_width: "500",
                    plugin_preview_height: "500"
                };

                var updateFilterWithDelay = function (item) {
                    ContentItem.isUpdating = false;
                    ContentItem.isItemValid = isValidFilter(ContentItem.filter);
                    if (!ContentItem.isUpdating && ContentItem.isItemValid) {
                        setTimeout(function () {
                            if (item.id) {
                                ContentItem.updateItemData();
                                $scope.$digest();
                            } /*else if (!ContentHome.isNewItemInserted) {
                             ContentHome.addNewItem();
                             }*/
                        }, 300);
                    }
                };

            ContentItem.updateItemData = function () {
                    Buildfire.datastore.update(ContentItem.filter.id, ContentItem.filter, TAG_NAMES.COUPON_CATEGORIES, function (err) {
                        ContentItem.isUpdating = false;
                        init();
                        if (err)
                            return console.error('There was a problem saving your data');
                    })
                };

                $scope.$watch(function () {
                    return ContentItem.item;
                }, updateItemsWithDelay, true);

                /*
                 * watch for changes in filters and trigger the saveDataWithDelay function on change
                 * */
                $scope.$watch(function () {
                    return ContentItem.filter;
                }, updateFilterWithDelay, true);

            }]);
})(window.buildfire, window.angular);<|endoftext|>


<commit_before>// @flow

import { assert } from 'chai';
import { spy } from 'sinon';
import ngrok from 'ngrok';
import proxyquire from 'proxyquire';
import run from '../../src/server/run';
import buildFixture from '../fixtures/build';

describe('integration: browserstack', () => {
  let consoleLog;
  let jsonReporter;
  let testUrl;

  function createDockerConfig(tests) {
    return {
      tests,
      testUrl,
      server: {
        host: 'localhost',
        port: 3090,
      },
      storage: {
        baseline: 'test/fixtures/simple/baseline',
        output: 'tmp/output',
      },
      selenium: {
        server: 'http://hub-cloud.browserstack.com/wd/hub',
      },
      profiles: {
        default: {
          desiredCapabilities: {
            'browserstack.user': process.env.BROWSERSTACK_USER,
            'browserstack.key': process.env.BROWSERSTACK_KEY,
            'browserstack.video': false,
            'browserstack.selenium_version': '3.0.1',
          },
        },
        osx_chrome: {
          desiredCapabilities: {
            browserName: 'chrome',
            os: 'OS X',
            os_version: 'Sierra',
          },
        },
        osx_safari: {
          desiredCapabilities: {
            browserName: 'safari',
            os: 'OS X',
            os_version: 'Sierra',
          },
        },
        edge: {
          desiredCapabilities: {
            browserName: 'edge',
            os: 'WINDOWS',
            os_version: '10',
          },
        },
      },
      reporters: [jsonReporter],
    };
  }

  beforeEach(() => {
    consoleLog = spy();
    jsonReporter = proxyquire('../../src/server/reporters/json', {
      './utils/logger': {
        log: consoleLog,
      },
    }).default;

    return new Promise((resolve) => {
      ngrok.connect(3090, (err, url) => {
        if (err) {
          throw err;
        } else {
          testUrl = url;
          resolve();
        }
      });
    });
  });

  afterEach(() => {
    ngrok.disconnect();
    ngrok.kill();
  });

  it('should pass both tests', function () {
    this.timeout(Infinity);

    return buildFixture('simple/tests.js')
      .then((stats) => {
        const tests = stats.compilation.assets['tests.js'].existsAt;
        const config = createDockerConfig(tests);

        return run(config);
      })
      .then(() => {

        assert.strictEqual(consoleLog.args.length, 3, 'there should be 3 result sets logged');

        const profiles = ['osx_chrome', 'osx_safari', 'edge'];

        Array(3).forEach((n, i) => {
          const results = JSON.parse(consoleLog.args[i][0]);
          const idx = profiles.indexOf(results.profile);
          assert.strictEqual(idx !== -1, true);
          profiles.splice(idx, 1);
          assert.strictEqual(results.total, 2);
          assert.strictEqual(results.passes, 2);
          assert.strictEqual(results.failures, 0);
        });
        return null;
      });
  });
});
<commit_msg>No padded blocks<commit_after>// @flow

import { assert } from 'chai';
import { spy } from 'sinon';
import ngrok from 'ngrok';
import proxyquire from 'proxyquire';
import run from '../../src/server/run';
import buildFixture from '../fixtures/build';

describe('integration: browserstack', () => {
  let consoleLog;
  let jsonReporter;
  let testUrl;

  function createDockerConfig(tests) {
    return {
      tests,
      testUrl,
      server: {
        host: 'localhost',
        port: 3090,
      },
      storage: {
        baseline: 'test/fixtures/simple/baseline',
        output: 'tmp/output',
      },
      selenium: {
        server: 'http://hub-cloud.browserstack.com/wd/hub',
      },
      profiles: {
        default: {
          desiredCapabilities: {
            'browserstack.user': process.env.BROWSERSTACK_USER,
            'browserstack.key': process.env.BROWSERSTACK_KEY,
            'browserstack.video': false,
            'browserstack.selenium_version': '3.0.1',
          },
        },
        osx_chrome: {
          desiredCapabilities: {
            browserName: 'chrome',
            os: 'OS X',
            os_version: 'Sierra',
          },
        },
        osx_safari: {
          desiredCapabilities: {
            browserName: 'safari',
            os: 'OS X',
            os_version: 'Sierra',
          },
        },
        edge: {
          desiredCapabilities: {
            browserName: 'edge',
            os: 'WINDOWS',
            os_version: '10',
          },
        },
      },
      reporters: [jsonReporter],
    };
  }

  beforeEach(() => {
    consoleLog = spy();
    jsonReporter = proxyquire('../../src/server/reporters/json', {
      './utils/logger': {
        log: consoleLog,
      },
    }).default;

    return new Promise((resolve) => {
      ngrok.connect(3090, (err, url) => {
        if (err) {
          throw err;
        } else {
          testUrl = url;
          resolve();
        }
      });
    });
  });

  afterEach(() => {
    ngrok.disconnect();
    ngrok.kill();
  });

  it('should pass both tests', function () {
    this.timeout(Infinity);

    return buildFixture('simple/tests.js')
      .then((stats) => {
        const tests = stats.compilation.assets['tests.js'].existsAt;
        const config = createDockerConfig(tests);

        return run(config);
      })
      .then(() => {
        assert.strictEqual(consoleLog.args.length, 3, 'there should be 3 result sets logged');

        const profiles = ['osx_chrome', 'osx_safari', 'edge'];

        Array(3).forEach((n, i) => {
          const results = JSON.parse(consoleLog.args[i][0]);
          const idx = profiles.indexOf(results.profile);
          assert.strictEqual(idx !== -1, true);
          profiles.splice(idx, 1);
          assert.strictEqual(results.total, 2);
          assert.strictEqual(results.passes, 2);
          assert.strictEqual(results.failures, 0);
        });
        return null;
      });
  });
});
<|endoftext|>


<commit_before>import { data } from './data.js';

function createTurtle(turtle) {
  const img = document.createElement('img');
  img.src = turtle.icon;
  img.height = 50;
  img.style.position = 'relative';
  img.style.transform = `translateX(${turtle.position}px)`;
  img.style.willChange = 'transform';
  img.style.top = `25px`;
  return img;
}

function createTracks(data) {
  const tracks = data.map(turtle => {
    const track = document.createElement('div');
    track.style.height = '100px';
    track.style.backgroundColor = turtle.color;
    track.append(createTurtle(turtle));
    track.addEventListener('click', (e) => {
      onClickTrack(e.target, turtle);
    });
    return track;
  });
  document.querySelector('#tracks').replaceChildren(...tracks);
}

function updateTracks(data) {
  Array.from(document.querySelectorAll('#tracks > div')).forEach((track, i) => {
    track.querySelector('img').style.transform = `translateX(${data[i].position}px)`;
  })
}

function getLead(data) {
  return data.reduce((max, curr) => {
    if (curr.position > max.position) {
      return curr;
    }
    return max;
  }, data[0]);
}

function tick(state, data, config) {
  const {limit, iconWidth} = config;
  const {gameTimer} = state;
  const lead = getLead(data);
  if (lead.position + lead.speed + iconWidth > limit) {
    state.winner = lead;
    state.isPlaying = false;
    setState(state);
    return;
  }
  for (let turtle of data) {
    if (!turtle.isPaused) {
      turtle.position += turtle.speed;
    }
  }
  updateTracks(data);
  gameTimer.id = requestAnimationFrame(() => tick(state, data, config), 1000 / 60);
  setState(state);
}

function onClickTrack(track, turtle) {
  turtle.isPaused = !turtle.isPaused;
}

function resetPosition(data, pos) {
  data.forEach(turtle => {
    turtle.position = pos;
  });
}

function start(state, data, config) {
  data = data.map(turtle => ({...turtle}));
  createTracks(data);
  resetPosition(data, 0);
  state.isPlaying = true;
  state.startTime = Date.now();
  tick(state, data, config);
}

function setState(state) {
  state = state;
  renderInterface(state);
}

function renderInterface(state) {
  const winnerEl = document.querySelector('#winner');
  const timeEl = document.querySelector('#time');
  const speedEl = document.querySelector('#speed');
  const restartBtn = document.querySelector('#restart');

  restartBtn.disabled = state.isPlaying;

  const time = (Date.now() - state.startTime) / 1000;
  timeEl.textContent = `${time}s`;

  if (state.winner) {
    winner.textContent = ` ${state.winner.name}!!!`;
    winnerEl.style.color = state.winner.color;
  }

  if (state.isPlaying) {
    winnerEl.textContent = '...';
    winnerEl.style.color = 'unset';
  } else {
    speedEl.textContent = `${(config.limit / time).toFixed(2)} px/s`;
  }
}

const config = {
  limit: document.querySelector('#tracks').offsetWidth,
  iconWidth: 50
};
const state = {
  gameTimer: {id: null},
  winner: null,
  isPlaying: false,
  startTime: null
};

window.addEventListener('resize', () => {
  config.limit = document.querySelector('#tracks').offsetWidth;
});
document.querySelector('#reset').addEventListener('click', () => resetPosition(data, 0));
document.querySelector('#restart').addEventListener('click', () => {
  clearTimeout(state.gameTimer.id);
  start(state, data, config);
});

start(state, data, config);<commit_msg>settimeout<commit_after>import { data } from './data.js';

function createTurtle(turtle) {
  const img = document.createElement('img');
  img.src = turtle.icon;
  img.height = 50;
  img.style.position = 'relative';
  img.style.transform = `translateX(${turtle.position}px)`;
  img.style.willChange = 'transform';
  img.style.top = `25px`;
  return img;
}

function createTracks(data) {
  const tracks = data.map(turtle => {
    const track = document.createElement('div');
    track.style.height = '100px';
    track.style.backgroundColor = turtle.color;
    track.append(createTurtle(turtle));
    track.addEventListener('click', (e) => {
      onClickTrack(e.target, turtle);
    });
    return track;
  });
  document.querySelector('#tracks').replaceChildren(...tracks);
}

function updateTracks(data) {
  Array.from(document.querySelectorAll('#tracks > div')).forEach((track, i) => {
    track.querySelector('img').style.transform = `translateX(${data[i].position}px)`;
  })
}

function getLead(data) {
  return data.reduce((max, curr) => {
    if (curr.position > max.position) {
      return curr;
    }
    return max;
  }, data[0]);
}

function tick(state, data, config) {
  const {limit, iconWidth} = config;
  const {gameTimer} = state;
  const lead = getLead(data);
  if (lead.position + lead.speed + iconWidth > limit) {
    state.winner = lead;
    state.isPlaying = false;
    setState(state);
    return;
  }
  for (let turtle of data) {
    if (!turtle.isPaused) {
      turtle.position += turtle.speed;
    }
  }
  updateTracks(data);
  gameTimer.id = setTimeout(() => tick(state, data, config), 1000 / 60);
  setState(state);
}

function onClickTrack(track, turtle) {
  turtle.isPaused = !turtle.isPaused;
}

function resetPosition(data, pos) {
  data.forEach(turtle => {
    turtle.position = pos;
  });
}

function start(state, data, config) {
  data = data.map(turtle => ({...turtle}));
  createTracks(data);
  resetPosition(data, 0);
  state.isPlaying = true;
  state.startTime = Date.now();
  tick(state, data, config);
}

function setState(state) {
  state = state;
  renderInterface(state);
}

function renderInterface(state) {
  const winnerEl = document.querySelector('#winner');
  const timeEl = document.querySelector('#time');
  const speedEl = document.querySelector('#speed');
  const restartBtn = document.querySelector('#restart');

  restartBtn.disabled = state.isPlaying;

  const time = (Date.now() - state.startTime) / 1000;
  timeEl.textContent = `${time}s`;

  if (state.winner) {
    winner.textContent = ` ${state.winner.name}!!!`;
    winnerEl.style.color = state.winner.color;
  }

  if (state.isPlaying) {
    winnerEl.textContent = '...';
    winnerEl.style.color = 'unset';
  } else {
    speedEl.textContent = `${(config.limit / time).toFixed(2)} px/s`;
  }
}

const config = {
  limit: document.querySelector('#tracks').offsetWidth,
  iconWidth: 50
};
const state = {
  gameTimer: {id: null},
  winner: null,
  isPlaying: false,
  startTime: null
};

window.addEventListener('resize', () => {
  config.limit = document.querySelector('#tracks').offsetWidth;
});
document.querySelector('#reset').addEventListener('click', () => resetPosition(data, 0));
document.querySelector('#restart').addEventListener('click', () => {
  clearTimeout(state.gameTimer.id);
  start(state, data, config);
});

start(state, data, config);<|endoftext|>