<commit_before>import constants from 'constants/AppConstants';

import range from 'lodash/range';
import capitalize from 'lodash/capitalize';
import moment from 'moment';
import PropTypes from 'prop-types';
import queryString from 'query-string';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { bindActionCreators } from 'redux';
import Button from 'react-bootstrap/lib/Button';
import Col from 'react-bootstrap/lib/Col';
import Grid from 'react-bootstrap/lib/Grid';
import Panel from 'react-bootstrap/lib/Panel';
import Row from 'react-bootstrap/lib/Row';

import { fetchPurposes } from 'actions/purposeActions';
import {
  changeSearchFilters,
  disableGeoposition,
  disableTimeRange,
  enableGeoposition,
  enableTimeRange,
} from 'actions/uiActions';
import { injectT } from 'i18n';
import CheckboxControl from './CheckboxControl';
import DatePickerControl from './DatePickerControl';
import PositionControl from './PositionControl';
import SearchBox from './SearchBox';
import searchControlsSelector from './searchControlsSelector';
import SelectControl from './SelectControl';
import TimeRangeControl from './TimeRangeControl';
import iconTimes from './images/times.svg';

class UnconnectedSearchControlsContainer extends Component {
  componentDidMount() {
    const { actions, urlSearchFilters } = this.props;
    actions.changeSearchFilters(urlSearchFilters);
    actions.fetchPurposes();
  }

  getPeopleCapacityOption(val) {
    const value = String(val);
    return { label: value, value };
  }

  getPeopleCapacityOptions() {
    const options = range(1, 10).map(this.getPeopleCapacityOption);
    range(10, 30, 5).forEach((value) => {
      options.push(this.getPeopleCapacityOption(value));
    });
    options.push({ label: '30+', value: '30' });
    return options;
  }

  getMunicipalityOptions = () => {
    let municipalities = constants.DEFAULT_MUNICIPALITY_OPTIONS;

    if (Array.isArray(SETTINGS.CUSTOM_MUNICIPALITY_OPTIONS)
    && SETTINGS.CUSTOM_MUNICIPALITY_OPTIONS.length) {
      municipalities = SETTINGS.CUSTOM_MUNICIPALITY_OPTIONS;
    }

    return municipalities.map((municipality) => {
      const municipalityStr = typeof municipality === 'string' ? municipality : municipality.toString();

      return {
        value: municipalityStr.toLowerCase(),
        label: capitalize(municipalityStr),
      };
    });
  };

  handleDateChange = ({ date }) => {
    const dateInCorrectFormat = moment(date, 'L').format(constants.DATE_FORMAT);
    this.handleFiltersChange({
      date: dateInCorrectFormat,
    });
  };

  handleFiltersChange = (newFilters) => {
    this.props.actions.changeSearchFilters(newFilters);
  };

  handlePositionSwitch = () => {
    if (!this.props.position) {
      this.props.actions.enableGeoposition();
    } else {
      this.props.actions.disableGeoposition();
    }
  };

  handleSearchBoxChange = (value) => {
    this.props.actions.changeSearchFilters({ search: value });
  };

  handleTimeRangeChange = ({ duration, end, start }) => {
    this.handleFiltersChange({
      duration,
      end,
      start,
    });
  };

  handleTimeRangeSwitch = (value) => {
    if (value) {
      this.props.actions.enableTimeRange();
    } else {
      this.props.actions.disableTimeRange();
    }
  };

  handleSearch = (newFilters = {}) => {
    const page = 1;
    const filters = { ...this.props.filters, ...newFilters, page };
    this.props.history.push(`/search?${queryString.stringify(filters)}`);
  };

  handleReset = () => {
    const emptyFilters = Object.assign({}, constants.SUPPORTED_SEARCH_FILTERS);
    if (this.props.position) {
      this.props.actions.disableGeoposition();
    }

    this.handleFiltersChange(emptyFilters);
    this.handleSearch(emptyFilters);
  };

  hasAdvancedFilters() {
    const { filters, position } = this.props;
    let hasFilters = Boolean(position);
    ['freeOfCharge', 'end', 'distance', 'duration', 'purpose', 'start', 'unit'].forEach((key) => {
      if (filters[key]) {
        hasFilters = true;
      }
    });
    return hasFilters;
  }

  render() {
    const {
      currentLanguage,
      filters,
      isFetchingPurposes,
      isFetchingUnits,
      purposeOptions,
      t,
      unitOptions,
    } = this.props;
    const peopleCapacityOptions = this.getPeopleCapacityOptions();
    const searchBoxOptions = purposeOptions.concat(unitOptions);
    const hasFilters = this.hasAdvancedFilters();

    return (

        <Grid>

{t('SearchControlsContainer.title')}

            <Row>
              <Col className="app-SearchControlsContainer__control" md={6} sm={12}>
                <SearchBox
                  onChange={this.handleSearchBoxChange}
                  onSearch={this.handleSearch}
                  options={searchBoxOptions}
                  value={filters.search}
                />
              </Col>
              <Col className="app-SearchControlsContainer__control" md={6} sm={12}>
                <DatePickerControl
                  currentLanguage={currentLanguage}
                  date={moment(filters.date).format('L')}
                  onConfirm={this.handleDateChange}
                />
              </Col>
            </Row>
            <Panel
              defaultExpanded={hasFilters}
              header={t('SearchControlsContainer.advancedSearch')}
            >
              <Row>
                <Col md={12}>
                  <SelectControl
                    id="municipality"
                    isLoading={isFetchingUnits}
                    isMulti
                    label={t('SearchControlsContainer.municipalityLabel')}
                    name="app-SearchControls-municipality-select"
                    onChange={municipalities => this.handleFiltersChange(
                      { municipality: municipalities.map(mun => mun.value) }
                    )}
                    options={this.getMunicipalityOptions()}
                    value={filters.municipality}
                  />
                </Col>
              </Row>
              <Row>
                <Col className="app-SearchControlsContainer__control" md={4} sm={12}>
                  <SelectControl
                    id="purpose"
                    isLoading={isFetchingPurposes}
                    label={t('SearchControlsContainer.purposeLabel')}
                    name="app-SearchControls-purpose-select"
                    onChange={purpose => this.handleFiltersChange({ purpose: purpose.value })}
                    options={purposeOptions}
                    value={filters.purpose}
                  />
                </Col>
                <Col className="app-SearchControlsContainer__control" md={4} sm={6}>
                  <SelectControl
                    id="unit"
                    isLoading={isFetchingUnits}
                    label={t('SearchControlsContainer.unitLabel')}
                    name="app-SearchControls-unit-select"
                    onChange={unit => this.handleFiltersChange({ unit: unit.value })}
                    options={unitOptions}
                    value={filters.unit}
                  />
                </Col>
                <Col className="app-SearchControlsContainer__control" md={4} sm={6}>
                  <SelectControl
                    id="people"
                    isLoading={isFetchingUnits}
                    label={t('SearchControlsContainer.peopleCapacityLabel')}
                    name="app-SearchControls-people-select"
                    onChange={people => this.handleFiltersChange({ people: people.value })}
                    options={peopleCapacityOptions}
                    value={filters.people ? String(parseInt(filters.people, 10)) : ''}
                  />
                </Col>
              </Row>
              <Row>
                <Col className="app-SearchControlsContainer__control" md={4} sm={6}>
                  <PositionControl
                    geolocated={Boolean(this.props.position)}
                    onConfirm={distance => this.handleFiltersChange({ distance })}
                    onPositionSwitch={this.handlePositionSwitch}
                    value={parseInt(filters.distance, 10)}
                  />
                </Col>
                <Col className="app-SearchControlsContainer__control" md={4} sm={6}>
                  <TimeRangeControl
                    duration={parseInt(filters.duration, 10)}
                    end={filters.end}
                    onConfirm={this.handleTimeRangeChange}
                    onTimeRangeSwitch={this.handleTimeRangeSwitch}
                    start={filters.start}
                    useTimeRange={filters.useTimeRange}
                  />
                </Col>
                <Col className="app-SearchControlsContainer__control" md={4} sm={12}>
                  <CheckboxControl
                    id="charge"
                    label={t('SearchControlsContainer.chargeLabel')}
                    labelClassName="app-SearchControlsCheckbox__label"
                    onConfirm={value => this.handleFiltersChange({ freeOfCharge: value })}
                    toggleClassName="app-SearchControlsCheckbox__toggle"
                    value={!!filters.freeOfCharge}
                  />
                </Col>
              </Row>
            </Panel>
            <Row className="app-SearchControlsContainer__buttons">
              <Col sm={12}>
                <Button
                  bsStyle="primary"
                  className="app-SearchControlsContainer__submit-button"
                  key="submit-button"
                  onClick={() => this.handleSearch()}
                  type="submit"
                >
                  {t('SearchControlsContainer.searchButton')}
                </Button>
                {hasFilters && (
                  <Button
                    bsStyle="link"
                    className="app-SearchControlsContainer__reset-button"
                    key="reset-button"
                    onClick={this.handleReset}
                  >

                    {t('SearchControlsContainer.resetButton')}
                  </Button>
                )}
              </Col>
            </Row>

        </Grid>

    );
  }
}

UnconnectedSearchControlsContainer.propTypes = {
  actions: PropTypes.object.isRequired,
  currentLanguage: PropTypes.string.isRequired,
  filters: PropTypes.object.isRequired,
  isFetchingPurposes: PropTypes.bool.isRequired,
  isFetchingUnits: PropTypes.bool.isRequired,
  position: PropTypes.object,
  purposeOptions: PropTypes.array.isRequired,
  t: PropTypes.func.isRequired,
  unitOptions: PropTypes.array.isRequired,
  urlSearchFilters: PropTypes.object.isRequired,
  history: PropTypes.object.isRequired,
};

UnconnectedSearchControlsContainer = injectT(UnconnectedSearchControlsContainer); // eslint-disable-line

function mapDispatchToProps(dispatch) {
  const actionCreators = {
    changeSearchFilters,
    disableGeoposition,
    disableTimeRange,
    enableGeoposition,
    enableTimeRange,
    fetchPurposes,
  };

  return { actions: bindActionCreators(actionCreators, dispatch) };
}

export { UnconnectedSearchControlsContainer };
export default withRouter(
  connect(
    searchControlsSelector,
    mapDispatchToProps
  )(UnconnectedSearchControlsContainer)
);
<commit_msg>add municipality to filter reset list<commit_after>import constants from 'constants/AppConstants';

import range from 'lodash/range';
import capitalize from 'lodash/capitalize';
import isEmpty from 'lodash/isEmpty';
import moment from 'moment';
import PropTypes from 'prop-types';
import queryString from 'query-string';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { bindActionCreators } from 'redux';
import Button from 'react-bootstrap/lib/Button';
import Col from 'react-bootstrap/lib/Col';
import Grid from 'react-bootstrap/lib/Grid';
import Panel from 'react-bootstrap/lib/Panel';
import Row from 'react-bootstrap/lib/Row';

import { fetchPurposes } from 'actions/purposeActions';
import {
  changeSearchFilters,
  disableGeoposition,
  disableTimeRange,
  enableGeoposition,
  enableTimeRange,
} from 'actions/uiActions';
import { injectT } from 'i18n';
import CheckboxControl from './CheckboxControl';
import DatePickerControl from './DatePickerControl';
import PositionControl from './PositionControl';
import SearchBox from './SearchBox';
import searchControlsSelector from './searchControlsSelector';
import SelectControl from './SelectControl';
import TimeRangeControl from './TimeRangeControl';
import iconTimes from './images/times.svg';

class UnconnectedSearchControlsContainer extends Component {
  componentDidMount() {
    const { actions, urlSearchFilters } = this.props;
    actions.changeSearchFilters(urlSearchFilters);
    actions.fetchPurposes();
  }

  getPeopleCapacityOption(val) {
    const value = String(val);
    return { label: value, value };
  }

  getPeopleCapacityOptions() {
    const options = range(1, 10).map(this.getPeopleCapacityOption);
    range(10, 30, 5).forEach((value) => {
      options.push(this.getPeopleCapacityOption(value));
    });
    options.push({ label: '30+', value: '30' });
    return options;
  }

  getMunicipalityOptions = () => {
    let municipalities = constants.DEFAULT_MUNICIPALITY_OPTIONS;

    if (Array.isArray(SETTINGS.CUSTOM_MUNICIPALITY_OPTIONS)
    && SETTINGS.CUSTOM_MUNICIPALITY_OPTIONS.length) {
      municipalities = SETTINGS.CUSTOM_MUNICIPALITY_OPTIONS;
    }

    return municipalities.map((municipality) => {
      const municipalityStr = typeof municipality === 'string' ? municipality : municipality.toString();

      return {
        value: municipalityStr.toLowerCase(),
        label: capitalize(municipalityStr),
      };
    });
  };

  handleDateChange = ({ date }) => {
    const dateInCorrectFormat = moment(date, 'L').format(constants.DATE_FORMAT);
    this.handleFiltersChange({
      date: dateInCorrectFormat,
    });
  };

  handleFiltersChange = (newFilters) => {
    this.props.actions.changeSearchFilters(newFilters);
  };

  handlePositionSwitch = () => {
    if (!this.props.position) {
      this.props.actions.enableGeoposition();
    } else {
      this.props.actions.disableGeoposition();
    }
  };

  handleSearchBoxChange = (value) => {
    this.props.actions.changeSearchFilters({ search: value });
  };

  handleTimeRangeChange = ({ duration, end, start }) => {
    this.handleFiltersChange({
      duration,
      end,
      start,
    });
  };

  handleTimeRangeSwitch = (value) => {
    if (value) {
      this.props.actions.enableTimeRange();
    } else {
      this.props.actions.disableTimeRange();
    }
  };

  handleSearch = (newFilters = {}) => {
    const page = 1;
    const filters = { ...this.props.filters, ...newFilters, page };
    this.props.history.push(`/search?${queryString.stringify(filters)}`);
  };

  handleReset = () => {
    const emptyFilters = Object.assign({}, constants.SUPPORTED_SEARCH_FILTERS);
    if (this.props.position) {
      this.props.actions.disableGeoposition();
    }

    this.handleFiltersChange(emptyFilters);
    this.handleSearch(emptyFilters);
  };

  hasAdvancedFilters() {
    const { filters, position } = this.props;
    let hasFilters = Boolean(position);
    ['freeOfCharge', 'end', 'distance', 'duration', 'purpose', 'start', 'unit', 'municipality'].forEach((key) => {
      if (!isEmpty(filters[key])) {
        hasFilters = true;
      }
    });
    return hasFilters;
  }

  render() {
    const {
      currentLanguage,
      filters,
      isFetchingPurposes,
      isFetchingUnits,
      purposeOptions,
      t,
      unitOptions,
    } = this.props;
    const peopleCapacityOptions = this.getPeopleCapacityOptions();
    const searchBoxOptions = purposeOptions.concat(unitOptions);
    const hasFilters = this.hasAdvancedFilters();

    return (

        <Grid>

{t('SearchControlsContainer.title')}

            <Row>
              <Col className="app-SearchControlsContainer__control" md={6} sm={12}>
                <SearchBox
                  onChange={this.handleSearchBoxChange}
                  onSearch={this.handleSearch}
                  options={searchBoxOptions}
                  value={filters.search}
                />
              </Col>
              <Col className="app-SearchControlsContainer__control" md={6} sm={12}>
                <DatePickerControl
                  currentLanguage={currentLanguage}
                  date={moment(filters.date).format('L')}
                  onConfirm={this.handleDateChange}
                />
              </Col>
            </Row>
            <Panel
              defaultExpanded={hasFilters}
              header={t('SearchControlsContainer.advancedSearch')}
            >
              <Row>
                <Col md={12}>
                  <SelectControl
                    id="municipality"
                    isLoading={isFetchingUnits}
                    isMulti
                    label={t('SearchControlsContainer.municipalityLabel')}
                    name="app-SearchControls-municipality-select"
                    onChange={municipalities => this.handleFiltersChange(
                      { municipality: municipalities.map(mun => mun.value) }
                    )}
                    options={this.getMunicipalityOptions()}
                    value={filters.municipality}
                  />
                </Col>
              </Row>
              <Row>
                <Col className="app-SearchControlsContainer__control" md={4} sm={12}>
                  <SelectControl
                    id="purpose"
                    isLoading={isFetchingPurposes}
                    label={t('SearchControlsContainer.purposeLabel')}
                    name="app-SearchControls-purpose-select"
                    onChange={purpose => this.handleFiltersChange({ purpose: purpose.value })}
                    options={purposeOptions}
                    value={filters.purpose}
                  />
                </Col>
                <Col className="app-SearchControlsContainer__control" md={4} sm={6}>
                  <SelectControl
                    id="unit"
                    isLoading={isFetchingUnits}
                    label={t('SearchControlsContainer.unitLabel')}
                    name="app-SearchControls-unit-select"
                    onChange={unit => this.handleFiltersChange({ unit: unit.value })}
                    options={unitOptions}
                    value={filters.unit}
                  />
                </Col>
                <Col className="app-SearchControlsContainer__control" md={4} sm={6}>
                  <SelectControl
                    id="people"
                    isLoading={isFetchingUnits}
                    label={t('SearchControlsContainer.peopleCapacityLabel')}
                    name="app-SearchControls-people-select"
                    onChange={people => this.handleFiltersChange({ people: people.value })}
                    options={peopleCapacityOptions}
                    value={filters.people ? String(parseInt(filters.people, 10)) : ''}
                  />
                </Col>
              </Row>
              <Row>
                <Col className="app-SearchControlsContainer__control" md={4} sm={6}>
                  <PositionControl
                    geolocated={Boolean(this.props.position)}
                    onConfirm={distance => this.handleFiltersChange({ distance })}
                    onPositionSwitch={this.handlePositionSwitch}
                    value={parseInt(filters.distance, 10)}
                  />
                </Col>
                <Col className="app-SearchControlsContainer__control" md={4} sm={6}>
                  <TimeRangeControl
                    duration={parseInt(filters.duration, 10)}
                    end={filters.end}
                    onConfirm={this.handleTimeRangeChange}
                    onTimeRangeSwitch={this.handleTimeRangeSwitch}
                    start={filters.start}
                    useTimeRange={filters.useTimeRange}
                  />
                </Col>
                <Col className="app-SearchControlsContainer__control" md={4} sm={12}>
                  <CheckboxControl
                    id="charge"
                    label={t('SearchControlsContainer.chargeLabel')}
                    labelClassName="app-SearchControlsCheckbox__label"
                    onConfirm={value => this.handleFiltersChange({ freeOfCharge: value })}
                    toggleClassName="app-SearchControlsCheckbox__toggle"
                    value={!!filters.freeOfCharge}
                  />
                </Col>
              </Row>
            </Panel>
            <Row className="app-SearchControlsContainer__buttons">
              <Col sm={12}>
                <Button
                  bsStyle="primary"
                  className="app-SearchControlsContainer__submit-button"
                  key="submit-button"
                  onClick={() => this.handleSearch()}
                  type="submit"
                >
                  {t('SearchControlsContainer.searchButton')}
                </Button>
                {hasFilters && (
                  <Button
                    bsStyle="link"
                    className="app-SearchControlsContainer__reset-button"
                    key="reset-button"
                    onClick={this.handleReset}
                  >

                    {t('SearchControlsContainer.resetButton')}
                  </Button>
                )}
              </Col>
            </Row>

        </Grid>

    );
  }
}

UnconnectedSearchControlsContainer.propTypes = {
  actions: PropTypes.object.isRequired,
  currentLanguage: PropTypes.string.isRequired,
  filters: PropTypes.object.isRequired,
  isFetchingPurposes: PropTypes.bool.isRequired,
  isFetchingUnits: PropTypes.bool.isRequired,
  position: PropTypes.object,
  purposeOptions: PropTypes.array.isRequired,
  t: PropTypes.func.isRequired,
  unitOptions: PropTypes.array.isRequired,
  urlSearchFilters: PropTypes.object.isRequired,
  history: PropTypes.object.isRequired,
};

UnconnectedSearchControlsContainer = injectT(UnconnectedSearchControlsContainer); // eslint-disable-line

function mapDispatchToProps(dispatch) {
  const actionCreators = {
    changeSearchFilters,
    disableGeoposition,
    disableTimeRange,
    enableGeoposition,
    enableTimeRange,
    fetchPurposes,
  };

  return { actions: bindActionCreators(actionCreators, dispatch) };
}

export { UnconnectedSearchControlsContainer };
export default withRouter(
  connect(
    searchControlsSelector,
    mapDispatchToProps
  )(UnconnectedSearchControlsContainer)
);
<|endoftext|>


<commit_before>/**
 * use sass
 */

var path = require('path');
var url = require('url');

fis.config.set('project.exclude', [
    '**/_*.scss',
    '*/node_modules/**',
    'Makefile',
    'build.sh',
    'README.md',
    'trigger.js',
    'changelog/**'
]);

fis.config.set('modules.parser.scss', 'sass'); //启用fis-parser-sass插件
fis.config.set('roadmap.ext.scss', 'css'); //scss文件编译后产出为css文件
fis.config.set('framework', '{%FRAMEWORK%}');
fis.config.set('roadmap.domain', '{%DOMAIN%}');
fis.config.set('macro', {
    'namespace': '{%FRAMEWORK%}',
    'class-warning': 'bs-callout bs-callout-warning',
    'class-danger': 'bs-callout bs-callout-danger',
    'class-info': 'bs-callout bs-callout-info'
});

fis.config.set('roadmap.path', [{
    reg: '*\.min\.(?:js|css)$',
    useOptimizer: false,
    release: '/static/${framework}/$&'
}, {
    reg: '**/_Sidebar.md',
    isNav: true
}, {
    reg: '**.md'
}, {
    reg: '/pages/${framework}/document.html',
    release: '/document.html',
    isDocumentPage: true
}, {
    // pages下的所有结构移至根目录
    reg: new RegExp('^\\/pages\\/' + fis.config.get('framework') + '\\/(.*\\.html)$'),
    release: '/$1'
}, {
    // 所有pages/,static/,widget/下的结构都相同，为./[repo-name]/[dirs/files]
    reg: new RegExp('^\\/(.*)\\/' + fis.config.get('framework') + '\\/(.*)$'),
    release: '/$1/$2'
}, {
    reg: 'lib/**'
}, {
    reg: 'map.json'
}, {
    reg: '**',
    // release: '/static/${framework}/$&'
    release: '/static/$&'
}]);

fis.config.set('settings.parser.sass', {
    'include_paths': [__dirname, path.join(__dirname, 'lib', 'bootstrap', 'stylesheets')]
});

// 获取导航需要编译加`-c`或者`-u`，因为我偷懒了，没有处理缓存
var gLinks = {};
var gNavRef = [];
fis.config.set('roadmap.ext.md', 'html');

fis.config.set('modules.parser.md', [function (content, file, conf) {
    var pangunode = require('pangunode');
    var ret = pangunode(content);

    if (ret && ret.length > 0) {
        return ret;
    }

    return ret;
}, function(content, file, conf) {
        var include_reg = /||$)/ig;
        var processed = [];

        function _process(content, file) {
            processed[file.realpath] = true;
            return content.replace(include_reg, function(all, $1) {
                if ($1) {
                    var f = fis.file(path.join(file.dirname, $1));
                    if (processed.indexOf(f.realpath) == -1) {
                        return _process(f.getContent(), f);
                    }
                    return f.getContent();
                }
                return all;
            });
        }
        return _process(content, file);
    },
    function(content, file, conf) {
        var marked = require('marked');
        var renderer = new marked.Renderer();
        var navs = [];
        var links = [];

        renderer.heading = function(text, level) {
            var link = {};
            link.text = encodeURI(text);
            link.level = level;
            var escapedText = link.text;

            links.push(link);

            if (level != 1) level += 1;
            return '<h' + level + ' class="' + (level == 1 ? 'page-header' : '') +
                (level == 3 ? '" id="' + text : '') +
                '">' +
                text + '</h' + level + '>';
        };

        renderer.link = function(href, title, text) {
            if (file.isNav) {
                var info = url.parse(href);
                if (!~navs.indexOf(info.pathname)) {
                    //check file exist?
                    var refFile = fis.file(file.dirname + '/' + info.pathname + '.md');
                    gNavRef.push(refFile.toString());
                    navs.push(info.pathname);
                }
                return;
            }

            var out = '<a href="' + href + '"';

            if (href.indexOf('#') != -1 && href.indexOf('http') != 0) {
                out = '' + text + '';
            return out;
        };

        marked.setOptions({
            renderer: renderer,
            highlight: function(code) {
                return require('highlight.js').highlightAuto(code).value;
            },
            langPrefix: 'hljs lang-',
            gfm: true,
            tables: true,
            breaks: false,
            pedantic: false,
            sanitize: false,
            smartLists: true,
            smartypants: false
        });

        content = marked(content);

        gLinks[file.realpath] = links;

        if (file.isNav) {
            return navs.map(function(path) {
                return '

';
            }).join('\n');
        }
        return content;
    }
]);

function getLinksHtml(links) {
    var ret = '';
    var flag = false;
    var levels = [];
    links.forEach(function(link) {
        if (link.level == 1) {
            if (flag) {
                ret += '

';
            }
            flag = true;
            ret += '
' + link.text + '
';
        } else if (link.level == 2) {
            ret += '
' + link.text + '';
        }
    });
    ret += '

';
    return ret;
}

fis.config.set('modules.postpackager', [function(ret, settings, conf, opt) {
    fis.util.map(ret.src, function(subpath, file) {
        if (file.isDocumentPage) {
            var useLinks = [];
            fis.util.map(gLinks, function(realpath, links) {
                if (gNavRef.indexOf(realpath) != -1) {
                    useLinks = useLinks.concat(links);
                }
            });
            console.log(useLinks);
            file.setContent(file.getContent().replace('<document_links>', getLinksHtml(useLinks)));
            gLinks = []; //reset
        }
        if (file.isHtmlLike) {
            var macro = fis.config.get('macro');
            var content = file.getContent();
            fis.util.map(macro, function(key, value) {
                content = content.replace(new RegExp('<#' + key + '#>', 'g'), function(all) {
                    return value;
                });
            });
            file.setContent(content);
        }
    });
},
'simple' //pack
]);

fis.config.set('settings.postpackager.simple.autoCombine', true);
fis.config.set('settings.postpackager.simple.autoReflow', true);
<commit_msg>bugfix<commit_after>/**
 * use sass
 */

var path = require('path');
var url = require('url');

fis.config.set('project.exclude', [
    '**/_*.scss',
    '*/node_modules/**',
    'Makefile',
    'build.sh',
    'README.md',
    'trigger.js',
    'changelog/**'
]);

fis.config.set('modules.parser.scss', 'sass'); //启用fis-parser-sass插件
fis.config.set('roadmap.ext.scss', 'css'); //scss文件编译后产出为css文件
fis.config.set('framework', '{%FRAMEWORK%}');
fis.config.set('roadmap.domain', '{%DOMAIN%}');
fis.config.set('macro', {
    'namespace': '{%FRAMEWORK%}',
    'class-warning': 'bs-callout bs-callout-warning',
    'class-danger': 'bs-callout bs-callout-danger',
    'class-info': 'bs-callout bs-callout-info'
});

fis.config.set('roadmap.path', [{
    reg: '*\.min\.(?:js|css)$',
    useOptimizer: false,
    release: '/static/${framework}/$&'
}, {
    reg: '**/_Sidebar.md',
    isNav: true
}, {
    reg: '**.md'
}, {
    reg: '/pages/${framework}/document.html',
    release: '/document.html',
    isDocumentPage: true
}, {
    // pages下的所有结构移至根目录
    reg: new RegExp('^\\/pages\\/' + fis.config.get('framework') + '\\/(.*\\.html)$'),
    release: '/$1'
}, {
    // 所有pages/,static/,widget/下的结构都相同，为./[repo-name]/[dirs/files]
    reg: new RegExp('^\\/(.*)\\/' + fis.config.get('framework') + '\\/(.*)$'),
    release: '/$1/$2'
}, {
    reg: 'lib/**'
}, {
    reg: 'map.json'
}, {
    reg: '**',
    // release: '/static/${framework}/$&'
    release: '/static/$&'
}]);

fis.config.set('settings.parser.sass', {
    'include_paths': [__dirname, path.join(__dirname, 'lib', 'bootstrap', 'stylesheets')]
});

// 获取导航需要编译加`-c`或者`-u`，因为我偷懒了，没有处理缓存
var gLinks = {};
var gNavRef = [];
fis.config.set('roadmap.ext.md', 'html');

fis.config.set('modules.parser.md', [function (content, file, conf) {
    var pangunode = require('pangunode');
    var ret = pangunode(content);

    if (ret && ret.length > 0) {
        return ret;
    }

    return ret;
}, function(content, file, conf) {
        var include_reg = /||$)/ig;
        var processed = [];

        function _process(content, file) {
            processed[file.realpath] = true;
            return content.replace(include_reg, function(all, $1) {
                if ($1) {
                    var f = fis.file(path.join(file.dirname, $1));
                    if (processed.indexOf(f.realpath) == -1) {
                        return _process(f.getContent(), f);
                    }
                    return f.getContent();
                }
                return all;
            });
        }
        return _process(content, file);
    },
    function(content, file, conf) {
        var marked = require('marked');
        var renderer = new marked.Renderer();
        var navs = [];
        var links = [];

        renderer.heading = function(text, level) {
            var link = {};
            link.text = text;
            link.level = level;
            var escapedText = encodeURI(text);

            links.push(link);

            if (level != 1) level += 1;
            return '<h' + level + ' class="' + (level == 1 ? 'page-header' : '') +
                (level == 3 ? '" id="' + text : '') +
                '">' +
                text + '</h' + level + '>';
        };

        renderer.link = function(href, title, text) {
            if (file.isNav) {
                var info = url.parse(href);
                if (!~navs.indexOf(info.pathname)) {
                    //check file exist?
                    var refFile = fis.file(file.dirname + '/' + info.pathname + '.md');
                    gNavRef.push(refFile.toString());
                    navs.push(info.pathname);
                }
                return;
            }

            var out = '<a href="' + href + '"';

            if (href.indexOf('#') != -1 && href.indexOf('http') != 0) {
                out = '' + text + '';
            return out;
        };

        marked.setOptions({
            renderer: renderer,
            highlight: function(code) {
                return require('highlight.js').highlightAuto(code).value;
            },
            langPrefix: 'hljs lang-',
            gfm: true,
            tables: true,
            breaks: false,
            pedantic: false,
            sanitize: false,
            smartLists: true,
            smartypants: false
        });

        content = marked(content);

        gLinks[file.realpath] = links;

        if (file.isNav) {
            return navs.map(function(path) {
                return '

';
            }).join('\n');
        }
        return content;
    }
]);

function getLinksHtml(links) {
    var ret = '';
    var flag = false;
    var levels = [];
    links.forEach(function(link) {
        if (link.level == 1) {
            if (flag) {
                ret += '

';
            }
            flag = true;
            ret += '
' + link.text + '
';
        } else if (link.level == 2) {
            ret += '
' + link.text + '';
        }
    });
    ret += '

';
    return ret;
}

fis.config.set('modules.postpackager', [function(ret, settings, conf, opt) {
    fis.util.map(ret.src, function(subpath, file) {
        if (file.isDocumentPage) {
            var useLinks = [];
            fis.util.map(gLinks, function(realpath, links) {
                if (gNavRef.indexOf(realpath) != -1) {
                    useLinks = useLinks.concat(links);
                }
            });
            console.log(useLinks);
            file.setContent(file.getContent().replace('<document_links>', getLinksHtml(useLinks)));
            gLinks = []; //reset
        }
        if (file.isHtmlLike) {
            var macro = fis.config.get('macro');
            var content = file.getContent();
            fis.util.map(macro, function(key, value) {
                content = content.replace(new RegExp('<#' + key + '#>', 'g'), function(all) {
                    return value;
                });
            });
            file.setContent(content);
        }
    });
},
'simple' //pack
]);

fis.config.set('settings.postpackager.simple.autoCombine', true);
fis.config.set('settings.postpackager.simple.autoReflow', true);
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var $ = require('jquery');
var Backbone = require('backbone');
var adminDispatcher = require('../../../dispatchers/admin-dispatcher');
var payments = require('../config.json');
var Model = require('../models/payment.js');
var session = require('../../../modules/session/models/session');

Backbone.$ = $;

var Payments = Backbone.Collection.extend({

  model: Model,

  baseUrl: "http://localhost:5000",

  comparator: function(a, b) {
    return b.id - a.id;
  },

  initialize: function() {
    _.bindAll(this);

    //register method with dispatcher
    adminDispatcher.register(this.dispatcherCallback);
  },

  dispatcherCallback: function(payload) {
    if (_.isUndefined(this[payload.actionType])) {
      return false;
    }

    this[payload.actionType](payload.data);
  },

  urlObject: {
    "/payments": {
      "path":"/v1/ripple_transactions",
      "method": "get"
    },
    "/payments/incoming": {
      "path":"/v1/ripple_transactions",
      "method": "get"
    },
    "/payments/completed": {
      "path":"/v1/ripple_transactions",
      "method": "get"
    },
    "/payments/failed": {
      "path":"/v1/ripple_transactions",
      "method": "get"
    },
    "/payments/outgoing": {
      "path":"/v1/ripple_transactions",
      "method": "get"
    },
    "/payments/new": {
      "path":"/v1/ripple_transactions",
      "method": "get"
    },
    "flagAsDone": {
      "path":"/v1/ripple_transactions/",
      "method": "save"
    }
  },

  updateUrl: function(page) {
    if (!page || _.isUndefined(this.urlObject[page])) {
      return false;
    }

    this.url = this.baseUrl + this.urlObject[page].path;
    this.httpMethod = this.urlObject[page].method;

    this.fetchData();
  },

  flagAsDone: function(id) {
    var model = this.get(id);

    model.set('url', (this.baseUrl + this.urlObject.flagAsDone.path + id));
    model.save('state', 'completed', {
      beforeSend: function(xhr) {
        xhr.setRequestHeader('Authorization', session.get('credentials'));
      }
    });
  },

  fetchData: function() {
    this.fetch({
      headers: {
        Authorization: session.get('credentials')
      }
    });
  },

  directionMap: {
    incoming: "from-ripple",
    outgoing: "to-ripple"
  },

  filterByDirection: function(direction) {
    return this.where({
      direction: this.directionMap[direction]
    });
  },

  //create fixture. delete when db ready
  getFakeType: function(data) {
    var output;

    output = _.map(data.ripple_transactions, function(model) {
      return _.extend({direction: Math.round(Math.random()) ? "to-ripple" : "from-ripple"}, model);
    });

    return {ripple_transactions: output};
  },

  parse: function(data) {
    // add fixture. Remove when db is ready
    data = this.getFakeType(data);

    return data.ripple_transactions;
  },

  sendPaymentComplete: function(paymentData) {
    var paymentModel = new this.model(paymentData);
    // set up listener for sync - check status: succeeded/failed

    this.add(paymentModel);

    paymentModel.pollStatus();
  }
});

module.exports = Payments;
<commit_msg>[FIX] Correct model url assignment<commit_after>'use strict';

var _ = require('lodash');
var $ = require('jquery');
var Backbone = require('backbone');
var adminDispatcher = require('../../../dispatchers/admin-dispatcher');
var payments = require('../config.json');
var Model = require('../models/payment.js');
var session = require('../../../modules/session/models/session');

Backbone.$ = $;

var Payments = Backbone.Collection.extend({

  model: Model,

  baseUrl: "http://localhost:5000",

  comparator: function(a, b) {
    return b.id - a.id;
  },

  initialize: function() {
    _.bindAll(this);

    //register method with dispatcher
    adminDispatcher.register(this.dispatcherCallback);
  },

  dispatcherCallback: function(payload) {
    if (_.isUndefined(this[payload.actionType])) {
      return false;
    }

    this[payload.actionType](payload.data);
  },

  urlObject: {
    "/payments": {
      "path":"/v1/ripple_transactions",
      "method": "get"
    },
    "/payments/incoming": {
      "path":"/v1/ripple_transactions",
      "method": "get"
    },
    "/payments/completed": {
      "path":"/v1/ripple_transactions",
      "method": "get"
    },
    "/payments/failed": {
      "path":"/v1/ripple_transactions",
      "method": "get"
    },
    "/payments/outgoing": {
      "path":"/v1/ripple_transactions",
      "method": "get"
    },
    "/payments/new": {
      "path":"/v1/ripple_transactions",
      "method": "get"
    },
    "flagAsDone": {
      "path":"/v1/ripple_transactions/",
      "method": "save"
    }
  },

  updateUrl: function(page) {
    if (!page || _.isUndefined(this.urlObject[page])) {
      return false;
    }

    this.url = this.baseUrl + this.urlObject[page].path;
    this.httpMethod = this.urlObject[page].method;

    this.fetchData();
  },

  flagAsDone: function(id) {
    var model = this.get(id);

    model.url = this.baseUrl + this.urlObject.flagAsDone.path + id;
    model.save('state', 'completed', {
      beforeSend: function(xhr) {
        xhr.setRequestHeader('Authorization', session.get('credentials'));
      }
    });
  },

  fetchData: function() {
    this.fetch({
      headers: {
        Authorization: session.get('credentials')
      }
    });
  },

  directionMap: {
    incoming: "from-ripple",
    outgoing: "to-ripple"
  },

  filterByDirection: function(direction) {
    return this.where({
      direction: this.directionMap[direction]
    });
  },

  //create fixture. delete when db ready
  getFakeType: function(data) {
    var output;

    output = _.map(data.ripple_transactions, function(model) {
      return _.extend({direction: Math.round(Math.random()) ? "to-ripple" : "from-ripple"}, model);
    });

    return {ripple_transactions: output};
  },

  parse: function(data) {
    // add fixture. Remove when db is ready
    data = this.getFakeType(data);

    return data.ripple_transactions;
  },

  sendPaymentComplete: function(paymentData) {
    var paymentModel = new this.model(paymentData);
    // set up listener for sync - check status: succeeded/failed

    this.add(paymentModel);

    paymentModel.pollStatus();
  }
});

module.exports = Payments;
<|endoftext|>


<commit_before>'use strict';

function fizzBuzz(input) {
  var output = [];
  var _output;

  for(var i in input) {
    if ((input[i] % 3 == 0) && (input[i] % 5 == 0)) {
      _output = 'FizzBuzz';
    } else if (input[i] % 3 == 0) {
      _output = 'Fizz';
    } else if (input[i] % 5 == 0) {
      _output = 'Buzz';
    } else {
      _output = input[i];
    }

    output.push(_output);
  }

  return output.join(' ');
}

function assert(condition, test) {
  var type = condition ? "PASS" : "FAIL";
  console.log(type + " >>> " + test);
}

function assertEqual(condition, test) {
  var output = condition == test ? "PASS" : "FAIL";

  if (output == "FAIL") {
    output += " >>> expecting \"" + condition + "\", \"" + test + "\" given";
  }

  console.log(output);
}

assertEqual(fizzBuzz([12, 13, 14, 15, 16]), 'Fizz 13 14 FizzBuzz 16');
assertEqual(fizzBuzz([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), '1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz');
assertEqual(fizzBuzz([45, 46, 47, 48, 49, 50]), 'FizzBuzz 46 47 Fizz 49 Buzz');<commit_msg>original fizzBuzz now also only process positive integer including zero (0) value. more test cases added.<commit_after>'use strict';

function fizzBuzz(input) {
  var output = [];
  var _output;

  for(var i in input) {
    _output = input[i];

    if (input[i] < 0) {
      // only take care of positive integer
      continue;
    } else if (input[i] == 0) {
      _output = 0;
    } else if ((input[i] % 3 == 0) && (input[i] % 5 == 0)) {
      _output = 'FizzBuzz';
    } else if (input[i] % 3 == 0) {
      _output = 'Fizz';
    } else if (input[i] % 5 == 0) {
      _output = 'Buzz';
    }

    output.push(_output);
  }

  return output.join(' ');
}

function assert(condition, test) {
  var type = condition ? "PASS" : "FAIL";
  console.log(type + " >>> " + test);
}

function assertEqual(condition, test) {
  var output = condition == test ? "PASS" : "FAIL";

  if (output == "FAIL") {
    output += " >>> expecting \"" + condition + "\", \"" + test + "\" given";
  }

  console.log(output);
}

assertEqual(fizzBuzz([-5, -4, -3, -2, -1, 0, 1, 2, 3]), '0 1 2 Fizz');
assertEqual(fizzBuzz([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]), '0 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11');
assertEqual(fizzBuzz([12, 13, 14, 15, 16]), 'Fizz 13 14 FizzBuzz 16');
assertEqual(fizzBuzz([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), '1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz');
assertEqual(fizzBuzz([45, 46, 47, 48, 49, 50]), 'FizzBuzz 46 47 Fizz 49 Buzz');<|endoftext|>


<commit_before>/**
 * A Data Center module to coordinate data between needed party and
 * the data modules. It is needed because we need a monitored way of
 * loading/refreshing required data instead of letting the needed party
 * implement their own (mostly ajax) in the editors and widgets.
 *
 * Fallback seq:
 * 1. url
 * 2. module name
 * 3. form field name (on the same form)
 *
 * @param {String} resource - A name/url/fieldname.
 * @param {Backbone.Form} form - optional only required when resource is fieldname.
 * @param {Function} cb - do something when the data is ready/resolved.
 *
 * @return {Object} - the parsed json object. (only the payload)
 *
 * @author Tim.Liu
 * @update 2013.04.08
 *
 */

(function(app){

	var module = app.module('DataCenter');

	/**
	 * Synced Data Resolving - Collection ONLY atm
	 */
	module.resolve = function(resource, form, cb){
		if(!cb){
			cb = form;
		}
		var options = {
			//signal the server that we will not use paginations
			data: {pagination: false},
			processData: true, //this will convert data into query params.
			success: function(data){
				//returned from url or collection fetch()
				cb(data.payload || data.toJSON());
			}
		};

		if(resource.indexOf('/') !== -1){
			//case: url
			$.ajax(_.extend({url: resource}).extend(options));

		}else if(/[A-Z].*/.test(resource) && app[resource]){
			//case: data module name
			app[resource].collection.fetch(options);

		}else if(form && !_.isFunction(form)){
			//case: form field
			//Note:: Make sure the request maker is rendered after the targeted field.
			cb(form.getValue(resource));

		}else{
			//unavailable >> undefined.
			Application.error('Data Resolving Error', resource, 'UNKNOWN...');
		}
	};

})(Application); /*Don't forget ;*/<commit_msg>Fixed bug in datacenter module.<commit_after>/**
 * A Data Center module to coordinate data between needed party and
 * the data modules. It is needed because we need a monitored way of
 * loading/refreshing required data instead of letting the needed party
 * implement their own (mostly ajax) in the editors and widgets.
 *
 * Fallback seq:
 * 1. url
 * 2. module name
 * 3. form field name (on the same form)
 *
 * @param {String} resource - A name/url/fieldname.
 * @param {Backbone.Form} form - optional only required when resource is fieldname.
 * @param {Function} cb - do something when the data is ready/resolved.
 *
 * @return {Object} - the parsed json object. (only the payload)
 *
 * @author Tim.Liu
 * @update 2013.04.08
 *
 */

(function(app){

	var module = app.module('DataCenter');

	/**
	 * Synced Data Resolving - Collection ONLY atm
	 */
	module.resolve = function(resource, form, cb){
		if(!cb){
			cb = form;
		}
		var options = {
			//signal the server that we will not use paginations
			data: {pagination: false},
			processData: true, //this will convert data into query params.
			success: function(data){
				//returned from url or collection fetch()
				cb(data.payload || data.toJSON());
			}
		};

		if(resource.indexOf('/') !== -1){
			//case: url
			$.ajax(_.extend(options,{url: resource}));

		}else if(/[A-Z].*/.test(resource) && app[resource]){
			//case: data module name
			app[resource].collection.fetch(options);

		}else if(form && !_.isFunction(form)){
			//case: form field
			//Note:: Make sure the request maker is rendered after the targeted field.
			cb(form.getValue(resource));

		}else{
			//unavailable >> undefined.
			Application.error('Data Resolving Error', resource, 'UNKNOWN...');
		}
	};

})(Application); /*Don't forget ;*/<|endoftext|>


<commit_before>import Ember from 'ember';
import ValidationEngine from 'ghost/mixins/validation-engine';

const {
    $,
    Controller,
    inject: {service, controller},
    isArray
} = Ember;

export default Controller.extend(ValidationEngine, {
    submitting: false,
    loggingIn: false,
    authProperties: ['identification', 'password'],

    ghostPaths: service(),
    notifications: service(),
    session: service(),
    application: controller(),
    ajax: service(),
    flowErrors: '',

    // ValidationEngine settings
    validationType: 'signin',

    actions: {
        authenticate() {
            let model = this.get('model');
            let authStrategy = 'authenticator:oauth2';

            // Authentication transitions to posts.index, we can leave spinner running unless there is an error
            this.get('session').authenticate(authStrategy, model.get('identification'), model.get('password')).catch((error) => {
                this.toggleProperty('loggingIn');

                if (error && error.errors) {
                    error.errors.forEach((err) => {
                        err.message = err.message.htmlSafe();
                    });

                    this.set('flowErrors', error.errors[0].message.string);

                    if (error.errors[0].message.string.match(/user with that email/)) {
                        this.get('model.errors').add('identification', '');
                    }

                    if (error.errors[0].message.string.match(/password is incorrect/)) {
                        this.get('model.errors').add('password', '');
                    }
                } else {
                    // Connection errors don't return proper status message, only req.body
                    this.get('notifications').showAlert('There was a problem on the server.', {type: 'error', key: 'session.authenticate.failed'});
                }
            });
        },

        validateAndAuthenticate() {
            this.set('flowErrors', '');
            // Manually trigger events for input fields, ensuring legacy compatibility with
            // browsers and password managers that don't send proper events on autofill
            $('#login').find('input').trigger('change');

            // This is a bit dirty, but there's no other way to ensure the properties are set as well as 'signin'
            this.get('hasValidated').addObjects(this.authProperties);
            this.validate({property: 'signin'}).then(() => {
                this.toggleProperty('loggingIn');
                this.send('authenticate');
            }).catch((error) => {
                if (error) {
                    this.get('notifications').showAPIError(error, {key: 'signin.authenticate'});
                } else {
                    this.set('flowErrors', 'Please fill out the form to sign in.');
                }
            });
        },

        forgotten() {
            let email = this.get('model.identification');
            let notifications = this.get('notifications');

            this.set('flowErrors', '');
            // This is a bit dirty, but there's no other way to ensure the properties are set as well as 'forgotPassword'
            this.get('hasValidated').addObject('identification');
            this.validate({property: 'forgotPassword'}).then(() => {
                let forgottenUrl = this.get('ghostPaths.url').api('authentication', 'passwordreset');
                this.toggleProperty('submitting');

                this.get('ajax').put(forgottenUrl, {
                    data: {
                        passwordreset: [{email}]
                    }
                }).then(() => {
                    this.toggleProperty('submitting');
                    notifications.showAlert('Please check your email for instructions.', {type: 'info', key: 'forgot-password.send.success'});
                }).catch((resp) => {
                    this.toggleProperty('submitting');

                    if (resp && resp.errors && isArray(resp.errors)) {
                        let [{message}] = resp.errors;

                        this.set('flowErrors', message);

                        if (message.match(/no user with that email/)) {
                            this.get('model.errors').add('identification', '');
                        }
                    } else {
                        notifications.showAPIError(resp, {defaultErrorText: 'There was a problem with the reset, please try again.', key: 'forgot-password.send'});
                    }
                });
            }).catch(() => {
                this.set('flowErrors', 'We need your email address to reset your password!');
            });
        }
    }
});
<commit_msg>Correctly use POST request to reset password<commit_after>import Ember from 'ember';
import ValidationEngine from 'ghost/mixins/validation-engine';

const {
    $,
    Controller,
    inject: {service, controller},
    isArray
} = Ember;

export default Controller.extend(ValidationEngine, {
    submitting: false,
    loggingIn: false,
    authProperties: ['identification', 'password'],

    ghostPaths: service(),
    notifications: service(),
    session: service(),
    application: controller(),
    ajax: service(),
    flowErrors: '',

    // ValidationEngine settings
    validationType: 'signin',

    actions: {
        authenticate() {
            let model = this.get('model');
            let authStrategy = 'authenticator:oauth2';

            // Authentication transitions to posts.index, we can leave spinner running unless there is an error
            this.get('session').authenticate(authStrategy, model.get('identification'), model.get('password')).catch((error) => {
                this.toggleProperty('loggingIn');

                if (error && error.errors) {
                    error.errors.forEach((err) => {
                        err.message = err.message.htmlSafe();
                    });

                    this.set('flowErrors', error.errors[0].message.string);

                    if (error.errors[0].message.string.match(/user with that email/)) {
                        this.get('model.errors').add('identification', '');
                    }

                    if (error.errors[0].message.string.match(/password is incorrect/)) {
                        this.get('model.errors').add('password', '');
                    }
                } else {
                    // Connection errors don't return proper status message, only req.body
                    this.get('notifications').showAlert('There was a problem on the server.', {type: 'error', key: 'session.authenticate.failed'});
                }
            });
        },

        validateAndAuthenticate() {
            this.set('flowErrors', '');
            // Manually trigger events for input fields, ensuring legacy compatibility with
            // browsers and password managers that don't send proper events on autofill
            $('#login').find('input').trigger('change');

            // This is a bit dirty, but there's no other way to ensure the properties are set as well as 'signin'
            this.get('hasValidated').addObjects(this.authProperties);
            this.validate({property: 'signin'}).then(() => {
                this.toggleProperty('loggingIn');
                this.send('authenticate');
            }).catch((error) => {
                if (error) {
                    this.get('notifications').showAPIError(error, {key: 'signin.authenticate'});
                } else {
                    this.set('flowErrors', 'Please fill out the form to sign in.');
                }
            });
        },

        forgotten() {
            let email = this.get('model.identification');
            let notifications = this.get('notifications');

            this.set('flowErrors', '');
            // This is a bit dirty, but there's no other way to ensure the properties are set as well as 'forgotPassword'
            this.get('hasValidated').addObject('identification');
            this.validate({property: 'forgotPassword'}).then(() => {
                let forgottenUrl = this.get('ghostPaths.url').api('authentication', 'passwordreset');
                this.toggleProperty('submitting');

                this.get('ajax').post(forgottenUrl, {
                    data: {
                        passwordreset: [{email}]
                    }
                }).then(() => {
                    this.toggleProperty('submitting');
                    notifications.showAlert('Please check your email for instructions.', {type: 'info', key: 'forgot-password.send.success'});
                }).catch((resp) => {
                    this.toggleProperty('submitting');

                    if (resp && resp.errors && isArray(resp.errors)) {
                        let [{message}] = resp.errors;

                        this.set('flowErrors', message);

                        if (message.match(/no user with that email/)) {
                            this.get('model.errors').add('identification', '');
                        }
                    } else {
                        notifications.showAPIError(resp, {defaultErrorText: 'There was a problem with the reset, please try again.', key: 'forgot-password.send'});
                    }
                });
            }).catch(() => {
                this.set('flowErrors', 'We need your email address to reset your password!');
            });
        }
    }
});
<|endoftext|>


<commit_before>const {retrieve, create} = require('./api/stripeRequests');
const api = require('./api');

module.exports = class StripePaymentProcessor {
    constructor(config, storage) {
        this.storage = storage;
        this._ready = new Promise((resolve, reject) => {
            this._resolveReady = resolve;
            this._rejectReady = reject;
        });
        this._configure(config);
    }

    async ready() {
        return this._ready;
    }

    async _configure(config) {
        this._stripe = require('stripe')(config.secretKey);
        this._stripe.__TEST_MODE__ = config.secretKey.startsWith('sk_test_');
        this._public_token = config.publicKey;
        this._checkoutSuccessUrl = config.checkoutSuccessUrl;
        this._checkoutCancelUrl = config.checkoutCancelUrl;
        this._webhookHandlerUrl = config.webhookHandlerUrl;

        try {
            this._product = await api.products.ensure(this._stripe, config.product);

            this._plans = [];
            for (const planSpec of config.plans) {
                const plan = await api.plans.ensure(this._stripe, planSpec, this._product);
                this._plans.push(plan);
            }

            try {
                // @TODO Need to somehow not duplicate this every time we boot
                const webhook = await create(this._stripe, 'webhookEndpoints', {
                    url: this._webhookHandlerUrl,
                    enabled_events: ['checkout.session.completed']
                });
                this._webhookSecret = webhook.secret;
            } catch (err) {
                console.log(err);
                this._webhookSecret = process.env.WEBHOOK_SECRET;
            }
        } catch (err) {
            return this._rejectReady(err);
        }

        return this._resolveReady({
            product: this._product,
            plans: this._plans
        });
    }

    async parseWebhook(body, signature) {
        return this._stripe.webhooks.constructEvent(body, signature, this._webhookSecret);
    }

    async createCheckoutSession(member, planName) {
        const customer = await api.customers.ensure(this._stripe, member, member.email);
        const plan = this._plans.find(plan => plan.nickname === planName);
        const session = await this._stripe.checkout.sessions.create({
            payment_method_types: ['card'],
            success_url: this._checkoutSuccessUrl,
            cancel_url: this._checkoutCancelUrl,
            customer: customer.id,
            subscription_data: {
                items: [{
                    plan: plan.id
                }]
            }
        });

        return session;
    }

    async addCustomerToMember(member, customer) {
        const metadata = await this.storage.get(member);
        return this.storage.set(member, metadata.concat({
            customer_id: customer.id
        }));
    }

    async _customerForMember(member) {
        const metadata = await this.storage.get(member);

        for (const data in metadata) {
            const customer = await this.getCustomer(data.customer_id);
            if (!customer.deleted) {
                return customer;
            }
        }

        const customer = await create(this._stripe, 'customers', {
            email: member.email
        });

        await this.addCustomerToMember(member, customer);

        return customer;
    }

    async getCustomer(id) {
        return retrieve(this._stripe, 'customers', id);
    }
};
<commit_msg>Added method for getting active subscriptions<commit_after>const {retrieve, create} = require('./api/stripeRequests');
const api = require('./api');

module.exports = class StripePaymentProcessor {
    constructor(config, storage) {
        this.storage = storage;
        this._ready = new Promise((resolve, reject) => {
            this._resolveReady = resolve;
            this._rejectReady = reject;
        });
        this._configure(config);
    }

    async ready() {
        return this._ready;
    }

    async _configure(config) {
        this._stripe = require('stripe')(config.secretKey);
        this._stripe.__TEST_MODE__ = config.secretKey.startsWith('sk_test_');
        this._public_token = config.publicKey;
        this._checkoutSuccessUrl = config.checkoutSuccessUrl;
        this._checkoutCancelUrl = config.checkoutCancelUrl;
        this._webhookHandlerUrl = config.webhookHandlerUrl;

        try {
            this._product = await api.products.ensure(this._stripe, config.product);

            this._plans = [];
            for (const planSpec of config.plans) {
                const plan = await api.plans.ensure(this._stripe, planSpec, this._product);
                this._plans.push(plan);
            }

            try {
                // @TODO Need to somehow not duplicate this every time we boot
                const webhook = await create(this._stripe, 'webhookEndpoints', {
                    url: this._webhookHandlerUrl,
                    enabled_events: ['checkout.session.completed']
                });
                this._webhookSecret = webhook.secret;
            } catch (err) {
                console.log(err);
                this._webhookSecret = process.env.WEBHOOK_SECRET;
            }
        } catch (err) {
            return this._rejectReady(err);
        }

        return this._resolveReady({
            product: this._product,
            plans: this._plans
        });
    }

    async parseWebhook(body, signature) {
        return this._stripe.webhooks.constructEvent(body, signature, this._webhookSecret);
    }

    async createCheckoutSession(member, planName) {
        const customer = await api.customers.ensure(this._stripe, member, member.email);
        const plan = this._plans.find(plan => plan.nickname === planName);
        const session = await this._stripe.checkout.sessions.create({
            payment_method_types: ['card'],
            success_url: this._checkoutSuccessUrl,
            cancel_url: this._checkoutCancelUrl,
            customer: customer.id,
            subscription_data: {
                items: [{
                    plan: plan.id
                }]
            }
        });

        return session;
    }

    async getActiveSubscriptions(member) {
        const metadata = await this.storage.get(member);

        const customers = await Promise.all(metadata.map((data) => {
            return this.getCustomer(data.customer_id);
        }));

        return customers.reduce(function (subscriptions, customer) {
            if (customer.deleted) {
                return subscriptions;
            }
            return subscriptions.concat(customer.subscriptions.data.reduce(function (subscriptions, subscription) {
                // Subscription has more than one plan
                // was not created by us - ignore it.
                if (!subscription.plan) {
                    return subscriptions;
                }
                // Ignore cancelled subscriptions
                if (subscription.status === 'cancelled') {
                    return subscriptions;
                }
                // Ignore unpaid subscriptions
                if (subscription.status === 'unpaid') {
                    return subscriptions;
                }

                return subscriptions.concat([{
                    customer: customer.id,
                    subscription: subscription.id,
                    plan: subscription.plan.id,
                    name: subscription.plan.nickname,
                    amount: subscription.plan.amount,
                    validUntil: subscription.current_period_end
                }]);
            }, []));
        }, []);
    }

    async addCustomerToMember(member, customer) {
        const metadata = await this.storage.get(member);
        return this.storage.set(member, metadata.concat({
            customer_id: customer.id
        }));
    }

    async _customerForMember(member) {
        const metadata = await this.storage.get(member);

        for (const data in metadata) {
            const customer = await this.getCustomer(data.customer_id);
            if (!customer.deleted) {
                return customer;
            }
        }

        const customer = await create(this._stripe, 'customers', {
            email: member.email
        });

        await this.addCustomerToMember(member, customer);

        return customer;
    }

    async getCustomer(id) {
        return retrieve(this._stripe, 'customers', id);
    }
};
<|endoftext|>


<commit_before>/**
 * @preserve
 * flipsnap.js
 *
 * @version  0.2.2
 * @url http://pxgrid.github.com/js-flipsnap/
 *
 * Copyright 2011 PixelGrid, Inc.
 * Licensed under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 */

(function(window, document, undefined) {

var div = document.createElement('div');
var prefix = ['webkit', 'moz', 'o', 'ms'];
var saveProp = {};
var support = {};

support.transform3d = hasProp([
	'perspectiveProperty',
	'WebkitPerspective',
	'MozPerspective',
	'OPerspective',
	'msPerspective'
]);

support.transform = hasProp([
	'transformProperty',
	'WebkitTransform',
	'MozTransform',
	'OTransform',
	'msTransform'
]);

support.transition = hasProp([
	'transitionProperty',
	'WebkitTransitionProperty',
	'MozTransitionProperty',
	'OTransitionProperty',
	'msTransitionProperty'
]);

support.touch = 'ontouchstart' in window;

support.cssAnimation = (support.transform3d || support.transform) && support.transition;

var touchStartEvent = support.touch ? 'touchstart' : 'mousedown';
var touchMoveEvent = support.touch ? 'touchmove' : 'mousemove';
var touchEndEvent = support.touch ? 'touchend' : 'mouseup';

function Flipsnap(element, conf) {
	return (this instanceof Flipsnap)
		? this.init(element, conf)
		: new Flipsnap(element, conf);
}

Flipsnap.prototype.init = function(element, conf) {
	var self = this;

	self.element = element;
	if (typeof element === 'string') {
		self.element = document.querySelector(element);
	}

	self.conf = conf || {};
	self.currentPoint = 0;
	self.currentX = 0;
	self.animation = false;
	self.use3d = (self.conf.use3d === undefined)
		? support.transform3d
		: self.conf.use3d;

	if (support.cssAnimation) {
		self._setStyle({
			transitionProperty: getCSSVal('transform'),
			transitionTimingFunction: 'cubic-bezier(0,0,0.25,1)',
			transitionDuration: '0ms',
			transform: self._getTranslate(0)
		});
	}
	else {
		self._setStyle({
			position: 'relative',
			left: '0px'
		});
	}

	self.refresh();

	self.element.addEventListener(touchStartEvent, self, false);
	self.element.addEventListener(touchMoveEvent, self, false);
	self.element.addEventListener(touchEndEvent, self, false);

	return self;
};

Flipsnap.prototype.handleEvent = function(event) {
	var self = this;

	switch (event.type) {
		case touchStartEvent:
			self._touchStart(event);
			break;
		case touchMoveEvent:
			self._touchMove(event);
			break;
		case touchEndEvent:
			self._touchEnd(event);
			break;
		case 'click':
			self._click(event);
			break;
	}
};

Flipsnap.prototype.refresh = function() {
	var self = this;

	// setting max point
    // conf.point is backward compatibility. (deprecated)
	self.maxPoint = self.conf.maxPoint || self.conf.point || (function() {
		var childNodes = self.element.childNodes,
			itemLength = 0,
			i = 0,
			len = childNodes.length,
			node;
		for(; i < len; i++) {
			node = childNodes[i];
			if (node.nodeType === 1) {
				itemLength++;
			}
		}
		if (itemLength > 0) {
			itemLength--;
		}

		return itemLength;
	})();

	// setting distance
	self.distance = self.conf.distance || self.element.scrollWidth / (self.maxPoint + 1);

	// setting maxX
	self.maxX = -self.distance * self.maxPoint;

	self.moveToPoint();
};

Flipsnap.prototype.hasNext = function() {
	var self = this;

	return self.currentPoint < self.maxPoint;
};

Flipsnap.prototype.hasPrev = function() {
	var self = this;

	return self.currentPoint > 0;
};

Flipsnap.prototype.toNext = function() {
	var self = this;

	if (!self.hasNext()) {
		return;
	}

	self.moveToPoint(self.currentPoint + 1);
};

Flipsnap.prototype.toPrev = function() {
	var self = this;

	if (!self.hasPrev()) {
		return;
	}

	self.moveToPoint(self.currentPoint - 1);
};

Flipsnap.prototype.moveToPoint = function(point) {
	var self = this;

	var beforePoint = self.currentPoint;

	// not called from `refresh()`
	if (point === undefined) {
		point = self.currentPoint;
	}

	if (point < 0) {
		self.currentPoint = 0;
	}
	else if (point > self.maxPoint) {
		self.currentPoint = self.maxPoint;
	}
	else {
		self.currentPoint = parseInt(point, 10);
	}

	if (support.cssAnimation) {
		self._setStyle({ transitionDuration: '350ms' });
	}
	else {
		self.animation = true;
	}
	self._setX(- self.currentPoint * self.distance);

	if (beforePoint !== self.currentPoint) { // is move?
		triggerEvent(self.element, 'fsmoveend', true, false);
		triggerEvent(self.element, 'flipsnap.moveend', true, false); // backward compatibility (deprecated)
	}
};

Flipsnap.prototype._setX = function(x) {
	var self = this;

	self.currentX = x;
	if (support.cssAnimation) {
		self.element.style[ saveProp.transform ] = self._getTranslate(x);
	}
	else {
		if (self.animation) {
			self._animate(x);
		}
		else {
			self.element.style.left = x + 'px';
		}
	}
};

Flipsnap.prototype._touchStart = function(event) {
	var self = this;

	if (self.conf.touchDisable) {
		return;
	}

	if (support.cssAnimation) {
		self._setStyle({ transitionDuration: '0ms' });
	}
	else {
		self.animation = false;
	}
	self.scrolling = true;
	self.moveReady = false;
	self.startPageX = getPage(event, 'pageX');
	self.startPageY = getPage(event, 'pageY');
	self.basePageX = self.startPageX;
	self.directionX = 0;
	self.startTime = event.timeStamp;
};

Flipsnap.prototype._touchMove = function(event) {
	var self = this;

	if (!self.scrolling) {
		return;
	}

	var pageX = getPage(event, 'pageX'),
		pageY = getPage(event, 'pageY'),
		distX,
		newX,
		deltaX,
		deltaY;

	if (self.moveReady) {
		event.preventDefault();
		event.stopPropagation();

		distX = pageX - self.basePageX;
		newX = self.currentX + distX;
		if (newX >= 0 || newX < self.maxX) {
			newX = Math.round(self.currentX + distX / 3);
		}
		self._setX(newX);

		// When distX is 0, use one previous value.
		// For android firefox. When touchend fired, touchmove also
		// fired and distX is certainly set to 0.
		self.directionX =
			distX === 0 ? self.directionX :
			distX > 0 ? -1 : 1;
	}
	else {
		deltaX = Math.abs(pageX - self.startPageX);
		deltaY = Math.abs(pageY - self.startPageY);
		if (deltaX > 5) {
			event.preventDefault();
			event.stopPropagation();
			self.moveReady = true;
			self.element.addEventListener('click', self, true);
		}
		else if (deltaY > 5) {
			self.scrolling = false;
		}
	}

	self.basePageX = pageX;
};

Flipsnap.prototype._touchEnd = function(event) {
	var self = this;

	if (!self.scrolling) {
		return;
	}

	self.scrolling = false;

	var newPoint = -self.currentX / self.distance;
	newPoint =
		(self.directionX > 0) ? Math.ceil(newPoint) :
		(self.directionX < 0) ? Math.floor(newPoint) :
		Math.round(newPoint);

	self.moveToPoint(newPoint);

	setTimeout(function() {
		self.element.removeEventListener('click', self, true);
	}, 200);
};

Flipsnap.prototype._click = function(event) {
	var self = this;

	event.stopPropagation();
	event.preventDefault();
};

Flipsnap.prototype._setStyle = function(styles) {
	var self = this;
	var style = self.element.style;

	for (var prop in styles) {
		setStyle(style, prop, styles[prop]);
	}
};

Flipsnap.prototype._animate = function(x) {
	var self = this;

	var elem = self.element;
	var begin = +new Date();
	var from = parseInt(elem.style.left, 10);
	var to = x;
	var duration = 350;
	var easing = function(time, duration) {
		return -(time /= duration) * (time - 2);
	};
	var timer = setInterval(function() {
		var time = new Date() - begin;
		var pos, now;
		if (time > duration) {
			clearInterval(timer);
			now = to;
		}
		else {
			pos = easing(time, duration);
			now = pos * (to - from) + from;
		}
		elem.style.left = now + "px";
	}, 10);
};

Flipsnap.prototype.destroy = function() {
	var self = this;

	self.element.removeEventListener(touchStartEvent, self);
	self.element.removeEventListener(touchMoveEvent, self);
	self.element.removeEventListener(touchEndEvent, self);
};

Flipsnap.prototype._getTranslate = function(x) {
	var self = this;

	return self.use3d
		? 'translate3d(' + x + 'px, 0, 0)'
		: 'translate(' + x + 'px, 0)';
};

function getPage(event, page) {
	return support.touch ? event.changedTouches[0][page] : event[page];
}

function hasProp(props) {
	return some(props, function(prop) {
		return div.style[ prop ] !== undefined;
	});
}

function setStyle(style, prop, val) {
	var _saveProp = saveProp[ prop ];
	if (_saveProp) {
		style[ _saveProp ] = val;
	}
	else if (style[ prop ] !== undefined) {
		saveProp[ prop ] = prop;
		style[ prop ] = val;
	}
	else {
		some(prefix, function(_prefix) {
			var _prop = ucFirst(_prefix) + ucFirst(prop);
			if (style[ _prop ] !== undefined) {
				saveProp[ prop ] = _prop;
				style[ _prop ] = val;
				return true;
			}
		});
	}
}

function getCSSVal(prop) {
	if (div.style[ prop ] !== undefined) {
		return prop;
	}
	else {
		var ret;
		some(prefix, function(_prefix) {
			var _prop = ucFirst(_prefix) + ucFirst(prop);
			if (div.style[ _prop ] !== undefined) {
				ret = '-' + _prefix + '-' + prop;
				return true;
			}
		});
		return ret;
	}
}

function ucFirst(str) {
	return str.charAt(0).toUpperCase() + str.substr(1);
}

function some(ary, callback) {
	for (var i = 0, len = ary.length; i < len; i++) {
		if (callback(ary[i], i)) {
			return true;
		}
	}
	return false;
}

function triggerEvent(element, type, bubbles, cancelable) {
	var ev = document.createEvent('Event');
	ev.initEvent(type, bubbles, cancelable);
	element.dispatchEvent(ev);
}

window.Flipsnap = Flipsnap;

})(window, window.document);
<commit_msg>use3dがtrueのときでも3d非対応端末は3dを使わないようにする<commit_after>/**
 * @preserve
 * flipsnap.js
 *
 * @version  0.2.2
 * @url http://pxgrid.github.com/js-flipsnap/
 *
 * Copyright 2011 PixelGrid, Inc.
 * Licensed under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 */

(function(window, document, undefined) {

var div = document.createElement('div');
var prefix = ['webkit', 'moz', 'o', 'ms'];
var saveProp = {};
var support = {};

support.transform3d = hasProp([
	'perspectiveProperty',
	'WebkitPerspective',
	'MozPerspective',
	'OPerspective',
	'msPerspective'
]);

support.transform = hasProp([
	'transformProperty',
	'WebkitTransform',
	'MozTransform',
	'OTransform',
	'msTransform'
]);

support.transition = hasProp([
	'transitionProperty',
	'WebkitTransitionProperty',
	'MozTransitionProperty',
	'OTransitionProperty',
	'msTransitionProperty'
]);

support.touch = 'ontouchstart' in window;

support.cssAnimation = (support.transform3d || support.transform) && support.transition;

var touchStartEvent = support.touch ? 'touchstart' : 'mousedown';
var touchMoveEvent = support.touch ? 'touchmove' : 'mousemove';
var touchEndEvent = support.touch ? 'touchend' : 'mouseup';

function Flipsnap(element, conf) {
	return (this instanceof Flipsnap)
		? this.init(element, conf)
		: new Flipsnap(element, conf);
}

Flipsnap.prototype.init = function(element, conf) {
	var self = this;

	self.element = element;
	if (typeof element === 'string') {
		self.element = document.querySelector(element);
	}

	self.conf = conf || {};
	self.currentPoint = 0;
	self.currentX = 0;
	self.animation = false;
	self.use3d = support.transform3d;
	if (self.conf.use3d === false) {
		self.use3d = false;
	}

	if (support.cssAnimation) {
		self._setStyle({
			transitionProperty: getCSSVal('transform'),
			transitionTimingFunction: 'cubic-bezier(0,0,0.25,1)',
			transitionDuration: '0ms',
			transform: self._getTranslate(0)
		});
	}
	else {
		self._setStyle({
			position: 'relative',
			left: '0px'
		});
	}

	self.refresh();

	self.element.addEventListener(touchStartEvent, self, false);
	self.element.addEventListener(touchMoveEvent, self, false);
	self.element.addEventListener(touchEndEvent, self, false);

	return self;
};

Flipsnap.prototype.handleEvent = function(event) {
	var self = this;

	switch (event.type) {
		case touchStartEvent:
			self._touchStart(event);
			break;
		case touchMoveEvent:
			self._touchMove(event);
			break;
		case touchEndEvent:
			self._touchEnd(event);
			break;
		case 'click':
			self._click(event);
			break;
	}
};

Flipsnap.prototype.refresh = function() {
	var self = this;

	// setting max point
    // conf.point is backward compatibility. (deprecated)
	self.maxPoint = self.conf.maxPoint || self.conf.point || (function() {
		var childNodes = self.element.childNodes,
			itemLength = 0,
			i = 0,
			len = childNodes.length,
			node;
		for(; i < len; i++) {
			node = childNodes[i];
			if (node.nodeType === 1) {
				itemLength++;
			}
		}
		if (itemLength > 0) {
			itemLength--;
		}

		return itemLength;
	})();

	// setting distance
	self.distance = self.conf.distance || self.element.scrollWidth / (self.maxPoint + 1);

	// setting maxX
	self.maxX = -self.distance * self.maxPoint;

	self.moveToPoint();
};

Flipsnap.prototype.hasNext = function() {
	var self = this;

	return self.currentPoint < self.maxPoint;
};

Flipsnap.prototype.hasPrev = function() {
	var self = this;

	return self.currentPoint > 0;
};

Flipsnap.prototype.toNext = function() {
	var self = this;

	if (!self.hasNext()) {
		return;
	}

	self.moveToPoint(self.currentPoint + 1);
};

Flipsnap.prototype.toPrev = function() {
	var self = this;

	if (!self.hasPrev()) {
		return;
	}

	self.moveToPoint(self.currentPoint - 1);
};

Flipsnap.prototype.moveToPoint = function(point) {
	var self = this;

	var beforePoint = self.currentPoint;

	// not called from `refresh()`
	if (point === undefined) {
		point = self.currentPoint;
	}

	if (point < 0) {
		self.currentPoint = 0;
	}
	else if (point > self.maxPoint) {
		self.currentPoint = self.maxPoint;
	}
	else {
		self.currentPoint = parseInt(point, 10);
	}

	if (support.cssAnimation) {
		self._setStyle({ transitionDuration: '350ms' });
	}
	else {
		self.animation = true;
	}
	self._setX(- self.currentPoint * self.distance);

	if (beforePoint !== self.currentPoint) { // is move?
		triggerEvent(self.element, 'fsmoveend', true, false);
		triggerEvent(self.element, 'flipsnap.moveend', true, false); // backward compatibility (deprecated)
	}
};

Flipsnap.prototype._setX = function(x) {
	var self = this;

	self.currentX = x;
	if (support.cssAnimation) {
		self.element.style[ saveProp.transform ] = self._getTranslate(x);
	}
	else {
		if (self.animation) {
			self._animate(x);
		}
		else {
			self.element.style.left = x + 'px';
		}
	}
};

Flipsnap.prototype._touchStart = function(event) {
	var self = this;

	if (self.conf.touchDisable) {
		return;
	}

	if (support.cssAnimation) {
		self._setStyle({ transitionDuration: '0ms' });
	}
	else {
		self.animation = false;
	}
	self.scrolling = true;
	self.moveReady = false;
	self.startPageX = getPage(event, 'pageX');
	self.startPageY = getPage(event, 'pageY');
	self.basePageX = self.startPageX;
	self.directionX = 0;
	self.startTime = event.timeStamp;
};

Flipsnap.prototype._touchMove = function(event) {
	var self = this;

	if (!self.scrolling) {
		return;
	}

	var pageX = getPage(event, 'pageX'),
		pageY = getPage(event, 'pageY'),
		distX,
		newX,
		deltaX,
		deltaY;

	if (self.moveReady) {
		event.preventDefault();
		event.stopPropagation();

		distX = pageX - self.basePageX;
		newX = self.currentX + distX;
		if (newX >= 0 || newX < self.maxX) {
			newX = Math.round(self.currentX + distX / 3);
		}
		self._setX(newX);

		// When distX is 0, use one previous value.
		// For android firefox. When touchend fired, touchmove also
		// fired and distX is certainly set to 0.
		self.directionX =
			distX === 0 ? self.directionX :
			distX > 0 ? -1 : 1;
	}
	else {
		deltaX = Math.abs(pageX - self.startPageX);
		deltaY = Math.abs(pageY - self.startPageY);
		if (deltaX > 5) {
			event.preventDefault();
			event.stopPropagation();
			self.moveReady = true;
			self.element.addEventListener('click', self, true);
		}
		else if (deltaY > 5) {
			self.scrolling = false;
		}
	}

	self.basePageX = pageX;
};

Flipsnap.prototype._touchEnd = function(event) {
	var self = this;

	if (!self.scrolling) {
		return;
	}

	self.scrolling = false;

	var newPoint = -self.currentX / self.distance;
	newPoint =
		(self.directionX > 0) ? Math.ceil(newPoint) :
		(self.directionX < 0) ? Math.floor(newPoint) :
		Math.round(newPoint);

	self.moveToPoint(newPoint);

	setTimeout(function() {
		self.element.removeEventListener('click', self, true);
	}, 200);
};

Flipsnap.prototype._click = function(event) {
	var self = this;

	event.stopPropagation();
	event.preventDefault();
};

Flipsnap.prototype._setStyle = function(styles) {
	var self = this;
	var style = self.element.style;

	for (var prop in styles) {
		setStyle(style, prop, styles[prop]);
	}
};

Flipsnap.prototype._animate = function(x) {
	var self = this;

	var elem = self.element;
	var begin = +new Date();
	var from = parseInt(elem.style.left, 10);
	var to = x;
	var duration = 350;
	var easing = function(time, duration) {
		return -(time /= duration) * (time - 2);
	};
	var timer = setInterval(function() {
		var time = new Date() - begin;
		var pos, now;
		if (time > duration) {
			clearInterval(timer);
			now = to;
		}
		else {
			pos = easing(time, duration);
			now = pos * (to - from) + from;
		}
		elem.style.left = now + "px";
	}, 10);
};

Flipsnap.prototype.destroy = function() {
	var self = this;

	self.element.removeEventListener(touchStartEvent, self);
	self.element.removeEventListener(touchMoveEvent, self);
	self.element.removeEventListener(touchEndEvent, self);
};

Flipsnap.prototype._getTranslate = function(x) {
	var self = this;

	return self.use3d
		? 'translate3d(' + x + 'px, 0, 0)'
		: 'translate(' + x + 'px, 0)';
};

function getPage(event, page) {
	return support.touch ? event.changedTouches[0][page] : event[page];
}

function hasProp(props) {
	return some(props, function(prop) {
		return div.style[ prop ] !== undefined;
	});
}

function setStyle(style, prop, val) {
	var _saveProp = saveProp[ prop ];
	if (_saveProp) {
		style[ _saveProp ] = val;
	}
	else if (style[ prop ] !== undefined) {
		saveProp[ prop ] = prop;
		style[ prop ] = val;
	}
	else {
		some(prefix, function(_prefix) {
			var _prop = ucFirst(_prefix) + ucFirst(prop);
			if (style[ _prop ] !== undefined) {
				saveProp[ prop ] = _prop;
				style[ _prop ] = val;
				return true;
			}
		});
	}
}

function getCSSVal(prop) {
	if (div.style[ prop ] !== undefined) {
		return prop;
	}
	else {
		var ret;
		some(prefix, function(_prefix) {
			var _prop = ucFirst(_prefix) + ucFirst(prop);
			if (div.style[ _prop ] !== undefined) {
				ret = '-' + _prefix + '-' + prop;
				return true;
			}
		});
		return ret;
	}
}

function ucFirst(str) {
	return str.charAt(0).toUpperCase() + str.substr(1);
}

function some(ary, callback) {
	for (var i = 0, len = ary.length; i < len; i++) {
		if (callback(ary[i], i)) {
			return true;
		}
	}
	return false;
}

function triggerEvent(element, type, bubbles, cancelable) {
	var ev = document.createEvent('Event');
	ev.initEvent(type, bubbles, cancelable);
	element.dispatchEvent(ev);
}

window.Flipsnap = Flipsnap;

})(window, window.document);
<|endoftext|>